<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <title>
        
        Darklovy</title>
    <meta name="description" content>
    <meta name="keywords" content>
    <meta name="HandheldFriendly" content="True">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <link rel="shortcut icon" href="/">
    <link rel="alternate" href="/atom.xml" title="Darklovy">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta property="og:type" content="website">
<meta property="og:title" content="Darklovy">
<meta property="og:url" content="https://www.ttxxly.top/page/5/index.html">
<meta property="og:site_name" content="Darklovy">
<meta property="og:locale" content="en">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Darklovy">

    <link href="https://fonts.googleapis.com/css?family=Inconsolata|Titillium+Web" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
    <link href="//cdn.bootcss.com/node-waves/0.7.5/waves.min.css" rel="stylesheet">
    <link rel="stylesheet" href="/style.css">
    <script>
        function setLoadingBarProgress(num) {
            document.getElementById('loading-bar').style.width = num + "%";
        }
    </script>
</head>
</html>
<body>
  <div id="loading-bar-wrapper">
  <div id="loading-bar"></div>
</div>


  <script>setLoadingBarProgress(20)</script> 
  <header class="l_header">
	<div class='wrapper'>
		<div class="nav-main container container--flex">
			<a class="logo flat-box" href='/' >
				Darklovy
			</a>
			<div class='menu'>
				<ul class='h-list'>
					
						<li>
							<a class='flat-box nav-Home' href='/'>
								首页
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-archives' href='/archives'>
								归档
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-categories' href='/categories'>
								分类
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-tags' href='/tags'>
								标签
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-algorithm' href='https://book.algorithm.ttxxly.top/'>
								算法
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-python' href='https://book.python.ttxxly.top/'>
								python
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-about' href='/about'>
								关于
							</a>
						</li>
					
				</ul>
				<div class='underline'></div>
			</div>
			
				<div class="m_search">
					<form name="searchform" class="form u-search-form">
						<input type="text" class="input u-search-input" placeholder="Search" />
						<span class="icon icon-search"></span>
					</form>
				</div>
			
			<ul class='switcher h-list'>
				
					<li class='s-search'><a href='javascript:void(0)'><span class="icon icon-search flat-box"></span></a></li>
				
				<li class='s-menu'><a href='javascript:void(0)'><span class="icon icon-menu flat-box"></span></a></li>
			</ul>
		</div>
		
		<div class='nav-sub container container--flex'>
			<a class="logo" class="flat-box" href='javascript:void(0)'>
				Word of Forks
			</a>

			<ul class='switcher h-list'>
				<li class='s-comment'><a href='javascript:void(0)'><span class="icon icon-chat_bubble_outline flat-box"></span></a></li>
				<li class='s-top'><a href='javascript:void(0)'><span class="icon icon-arrow_upward flat-box"></span></a></li>
				<li class='s-toc'><a href='javascript:void(0)'><span class="icon icon-format_list_numbered flat-box"></span></a></li>
			</ul>
		</div>
	</div>
</header>
<aside class="menu-phone">
	<nav>
		
			<a href="/" class="nav-Home nav">
				首页
			</a>
		
			<a href="/archives" class="nav-archives nav">
				归档
			</a>
		
			<a href="/categories" class="nav-categories nav">
				分类
			</a>
		
			<a href="/tags" class="nav-tags nav">
				标签
			</a>
		
			<a href="https://book.algorithm.ttxxly.top/" class="nav-algorithm nav">
				算法
			</a>
		
			<a href="https://book.python.ttxxly.top/" class="nav-python nav">
				python
			</a>
		
			<a href="/about" class="nav-about nav">
				关于
			</a>
		
	</nav>
</aside>

    <script>setLoadingBarProgress(40);</script>
  <div class="l_body">
    <div class='container clearfix'>
      <div class='l_main'>
        
<section class="post-list">
	
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-虚拟机/">
        Java 面试题系列篇-虚拟机
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="虚拟机-（JVM）"><a href="#虚拟机-（JVM）" class="headerlink" title="虚拟机 （JVM）"></a>虚拟机 （JVM）</h2><h3 id="【JVM】你知道哪些或者你们线上使⽤什么GC策略？它有什么优势，适⽤于什么场景？"><a href="#【JVM】你知道哪些或者你们线上使⽤什么GC策略？它有什么优势，适⽤于什么场景？" class="headerlink" title="【JVM】你知道哪些或者你们线上使⽤什么GC策略？它有什么优势，适⽤于什么场景？"></a>【JVM】<strong>你知道哪些或者你们线上使⽤什么GC策略？它有什么优势，适⽤于什么场景？</strong></h3><p><a href="https://blog.csdn.net/chenleixing/article/details/46706039/" target="_blank" rel="noopener">https://blog.csdn.net/chenleixing/article/details/46706039/</a></p>
<h3 id="【JVM】Java类加载器包括⼏种？它们之间的⽗⼦关系是怎么样的？双亲委派机制是什么意思？有什么好处？"><a href="#【JVM】Java类加载器包括⼏种？它们之间的⽗⼦关系是怎么样的？双亲委派机制是什么意思？有什么好处？" class="headerlink" title="【JVM】Java类加载器包括⼏种？它们之间的⽗⼦关系是怎么样的？双亲委派机制是什么意思？有什么好处？"></a>【JVM】<strong>Java类加载器包括⼏种？它们之间的⽗⼦关系是怎么样的？双亲委派机制是什么意思？有什么好处？</strong></h3><p>启动Bootstrap类加载、扩展Extension类加载、系统System类加载。</p>
<p>父子关系如下：</p>
<p>启动类加载器 ，由C++ 实现，没有父类；</p>
<p>扩展类加载器，由Java语言实现，父类加载器为null；</p>
<p>系统类加载器，由Java语言实现，父类加载器为扩展类加载器；</p>
<p>自定义类加载器，父类加载器肯定为AppClassLoader。</p>
<p>双亲委派机制：类加载器收到类加载请求，自己不加载，向上委托给父类加载，父类加载不了，再自己加载。</p>
<p>优势避免Java核心API篡改。</p>
<p><a href="https://blog.csdn.net/javazejian/article/details/73413292/" target="_blank" rel="noopener">https://blog.csdn.net/javazejian/article/details/73413292/</a></p>
<h3 id="【JVM】如何⾃定义⼀个类加载器？你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗？"><a href="#【JVM】如何⾃定义⼀个类加载器？你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗？" class="headerlink" title="【JVM】如何⾃定义⼀个类加载器？你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗？"></a>【JVM】<strong>如何⾃定义⼀个类加载器？你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗？</strong></h3><p>自定义类加载的意义：</p>
<p>加载特定路径的class文件</p>
<p>加载一个加密的网络class文件</p>
<p>热部署加载class文件</p>
<h3 id="【JVM】jstack-是⼲什么的-jstat-呢？如果线上程序周期性地出现卡顿，你怀疑可-能是-GC-导致的，你会怎么来排查这个问题？线程⽇志⼀般你会看其中的什么-部分？"><a href="#【JVM】jstack-是⼲什么的-jstat-呢？如果线上程序周期性地出现卡顿，你怀疑可-能是-GC-导致的，你会怎么来排查这个问题？线程⽇志⼀般你会看其中的什么-部分？" class="headerlink" title="【JVM】jstack 是⼲什么的? jstat 呢？如果线上程序周期性地出现卡顿，你怀疑可 能是 GC 导致的，你会怎么来排查这个问题？线程⽇志⼀般你会看其中的什么 部分？"></a>【JVM】<strong>jstack 是⼲什么的? jstat 呢？如果线上程序周期性地出现卡顿，你怀疑可 能是 GC 导致的，你会怎么来排查这个问题？线程⽇志⼀般你会看其中的什么 部分？</strong></h3><p>jstack 用来查询 Java 进程的堆栈信息。</p>
<p>jvisualvm 监控内存泄露，跟踪垃圾回收、执行时内存、cpu分析、线程分析。</p>
<p>详见Java jvisualvm简要说明，可参考 线上FullGC频繁的排查。</p>
<p>Java jvisualvm简要说明</p>
<p><a href="https://blog.csdn.net/a19881029/article/details/8432368/" target="_blank" rel="noopener">https://blog.csdn.net/a19881029/article/details/8432368/</a></p>
<p>线上FullGC频繁的排查</p>
<p><a href="https://blog.csdn.net/wilsonpeng3/article/details/70064336/" target="_blank" rel="noopener">https://blog.csdn.net/wilsonpeng3/article/details/70064336/</a></p>
<h3 id="【JVM】StackOverflow异常有没有遇到过？⼀般你猜测会在什么情况下被触发？如何指定⼀个线程的堆栈⼤⼩？⼀般你们写多少？"><a href="#【JVM】StackOverflow异常有没有遇到过？⼀般你猜测会在什么情况下被触发？如何指定⼀个线程的堆栈⼤⼩？⼀般你们写多少？" class="headerlink" title="【JVM】StackOverflow异常有没有遇到过？⼀般你猜测会在什么情况下被触发？如何指定⼀个线程的堆栈⼤⼩？⼀般你们写多少？"></a>【JVM】<strong>StackOverflow异常有没有遇到过？⼀般你猜测会在什么情况下被触发？如何指定⼀个线程的堆栈⼤⼩？⼀般你们写多少？</strong></h3><p>栈内存溢出，一般由栈内存的局部变量过爆了，导致内存溢出。出现在递归方法，参数个数过多，递归过深，递归没有出口。</p>
<h3 id="什么是垃圾回收？"><a href="#什么是垃圾回收？" class="headerlink" title="什么是垃圾回收？"></a>什么是垃圾回收？</h3><p>垃圾回收是Java中自动内存管理的另一种叫法。垃圾回收的目的是为程序保持尽可能多的可用堆（heap）。 JVM会删除堆上不再需要从堆引用的对象。</p>
<h3 id="用一个例子解释垃圾回收？"><a href="#用一个例子解释垃圾回收？" class="headerlink" title="用一个例子解释垃圾回收？"></a>用一个例子解释垃圾回收？</h3><p>比方说，下面这个方法就会从函数调用。</p>
<p>void method(){</p>
<p>​    Calendar calendar = new GregorianCalendar(2000,10,30);</p>
<p>​    System.out.println(calendar);</p>
<p>}</p>
<p>通过函数第一行代码中参考变量calendar，在堆上创建了GregorianCalendar类的一个对象。</p>
<p>函数结束执行后，引用变量calendar不再有效。因此，在方法中没有创建引用到对象。</p>
<p>JVM认识到这一点，会从堆中删除对象。这就是所谓的垃圾回收。</p>
<h3 id="什么时候运行垃圾回收？"><a href="#什么时候运行垃圾回收？" class="headerlink" title="什么时候运行垃圾回收？"></a>什么时候运行垃圾回收？</h3><p>垃圾回收在JVM突发奇想和心血来潮时运行（没有那么糟糕）。运行垃圾收集的可能情况是：</p>
<p>堆可用内存不足</p>
<p>CPU空闲</p>
<h3 id="垃圾回收的最佳做法？"><a href="#垃圾回收的最佳做法？" class="headerlink" title="垃圾回收的最佳做法？"></a>垃圾回收的最佳做法？</h3><p>用编程的方式，我们可以要求（记住这只是一个请求——不是一个命令）JVM通过调用System.gc()方法来运行垃圾回收。</p>
<p>当内存已满，且堆上没有对象可用于垃圾回收时，JVM可能会抛出OutOfMemoryException。</p>
<p>对象在被垃圾回收从堆上删除之前，会运行finalize()方法。我们建议不要用finalize()方法写任何代码。</p>
<h3 id="Java-中会存在内存泄漏吗，请简单描述。"><a href="#Java-中会存在内存泄漏吗，请简单描述。" class="headerlink" title="Java 中会存在内存泄漏吗，请简单描述。"></a>Java 中会存在内存泄漏吗，请简单描述。</h3><p>答：理论上Java因为有垃圾回收机制（GC）不会存在内存泄露问题（这也是Java被广泛使用于服务器端编程的一个重要原因）；然而在实际开发中，可能会存在无用但可达的对象，这些对象不能被GC回收，因此也会导致内存泄露的发生。例如Hibernate的Session（一级缓存）中的对象属于持久态，垃圾回收器是不会回收这些对象的，然而这些对象中可能存在无用的垃圾对象，如果不及时关闭（close）或清空（flush）一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。</p>
<p>import java.util.Arrays;</p>
<p>import java.util.EmptyStackException;</p>
<p>public class MyStack<t> {</t></p>
<p>​    private T[] elements;</p>
<p>​    private int size = 0;</p>
<p>​    private static final int INIT_CAPACITY = 16;</p>
<p>​    public MyStack() {</p>
<p>​        elements = (T[]) new Object[INIT_CAPACITY];</p>
<p>​    }</p>
<p>​    public void push(T elem) {</p>
<p>​        ensureCapacity();</p>
<p>​        elements[size++] = elem;</p>
<p>​    }</p>
<p>​    public T pop() {</p>
<p>​        if(size == 0) </p>
<p>​            throw new EmptyStackException();</p>
<p>​        return elements[–size];</p>
<p>​    }</p>
<p>​    private void ensureCapacity() {</p>
<p>​        if(elements.length == size) {</p>
<p>​            elements = Arrays.copyOf(elements, 2 * size + 1);</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<p>上面的代码实现了一个栈（先进后出（FILO））结构，乍看之下似乎没有什么明显的问题，它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题，当我们用pop方法弹出栈中的对象时，该对象不会被当作垃圾回收，即使使用栈的程序不再引用这些对象，因为栈内部维护着对这些对象的过期引用（obsolete reference）。在支持垃圾回收的语言中，内存泄露是很隐蔽的，这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了，那么垃圾回收器不会处理这个对象，也不会处理该对象引用的其他对象，即使这样的对象只有少数几个，也可能会导致很多的对象被排除在垃圾回收之外，从而对性能造成重大影响，极端情况下会引发Disk Paging（物理内存与硬盘的虚拟内存交换数据），甚至造成OutOfMemoryError。</p>
<h3 id="GC是什么？为什么要有GC？"><a href="#GC是什么？为什么要有GC？" class="headerlink" title="GC是什么？为什么要有GC？"></a>GC是什么？为什么要有GC？</h3><p>答：GC是垃圾收集的意思，内存处理是编程人员容易出现问题的地方，忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃，Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的，Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一：System.gc() 或Runtime.getRuntime().gc() ，但JVM可以屏蔽掉显示的垃圾回收调用。<br>垃圾回收可以有效的防止内存泄露，有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行，不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收，程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期，垃圾回收是Java最大的亮点之一，因为服务器端的编程需要有效的防止内存泄露问题，然而时过境迁，如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验，其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。</p>
<p>补充：垃圾回收机制有很多种，包括：分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量，堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除，但是Java对其进行了改进，采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域，在垃圾收集过程中，可能会将对象移动到不同区域： </p>
<ul>
<li>伊甸园（Eden）：这是对象最初诞生的区域，并且对大多数对象来说，这里是它们唯一存在过的区域。 </li>
<li>幸存者乐园（Survivor）：从伊甸园幸存下来的对象会被挪到这里。 </li>
<li>终身颐养园（Tenured）：这是足够老的幸存对象的归宿。年轻代收集（Minor-GC）过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时，就会触发一次完全收集（Major-GC），这里可能还会牵扯到压缩，以便为大对象腾出足够的空间。</li>
</ul>
<p>与垃圾回收相关的JVM参数：</p>
<p>-Xms / -Xmx — 堆的初始大小 / 堆的最大大小</p>
<p>-Xmn — 堆中年轻代的大小</p>
<p>-XX:-DisableExplicitGC — 让System.gc()不产生任何作用</p>
<p>-XX:+PrintGCDetails — 打印GC的细节</p>
<p>-XX:+PrintGCDateStamps — 打印GC操作的时间戳</p>
<p>-XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小</p>
<p>-XX:NewRatio — 可以设置老生代和新生代的比例</p>
<p>-XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布</p>
<p>-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold：设置老年代阀值的初始值和最大值</p>
<p>-XX:TargetSurvivorRatio：设置幸存区的目标使用率</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-SpringMVC-框架/">
        Java 面试题系列篇-SpringMVC 框架
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="SpringMVC-框架"><a href="#SpringMVC-框架" class="headerlink" title="SpringMVC 框架"></a>SpringMVC 框架</h2><h3 id="如何在Web项目中配置Spring-MVC？"><a href="#如何在Web项目中配置Spring-MVC？" class="headerlink" title="如何在Web项目中配置Spring MVC？"></a>如何在Web项目中配置Spring MVC？</h3><p>答：要使用Spring MVC需要在Web项目配置文件中配置其前端控制器DispatcherServlet，如下所示：</p>
<p><web-app></web-app></p>
<p>​    <servlet></servlet></p>
<p>​        <servlet-name>example</servlet-name></p>
<p>​        <servlet-class></servlet-class></p>
<p>​            org.springframework.web.servlet.DispatcherServlet</p>
<p>​        </p>
<p>​        <load-on-startup>1</load-on-startup></p>
<p>​    </p>
<p>​    <servlet-mapping></servlet-mapping></p>
<p>​        <servlet-name>example</servlet-name></p>
<p>​        <url-pattern>*.html</url-pattern></p>
<p>​    </p>
<p></p>
<p>说明：上面的配置中使用了*.html的后缀映射，这样做一方面不能够通过URL推断采用了何种服务器端的技术，另一方面可以欺骗搜索引擎，因为搜索引擎不会搜索动态页面，这种做法称为伪静态化。</p>
<h3 id="Spring-MVC的工作原理是怎样的？"><a href="#Spring-MVC的工作原理是怎样的？" class="headerlink" title="Spring MVC的工作原理是怎样的？"></a>Spring MVC的工作原理是怎样的？</h3><p>答：Spring MVC的工作原理如下图所示：<br>① 客户端的所有请求都交给前端控制器DispatcherServlet来处理，它会负责调用系统的其他模块来真正处理用户的请求。<br>② DispatcherServlet收到请求后，将根据请求的信息（包括URL、HTTP协议方法、请求头、请求参数、Cookie等）以及HandlerMapping的配置找到处理该请求的Handler（任何一个对象都可以作为请求的Handler）。<br>③在这个地方Spring会通过HandlerAdapter对该处理器进行封装。<br>④ HandlerAdapter是一个适配器，它用统一的接口对各种Handler中的方法进行调用。<br>⑤ Handler完成对用户请求的处理后，会返回一个ModelAndView对象给DispatcherServlet，ModelAndView顾名思义，包含了数据模型以及相应的视图的信息。<br>⑥ ModelAndView的视图是逻辑视图，DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。<br>⑦ 当得到真正的视图对象后，DispatcherServlet会利用视图对象对模型数据进行渲染。<br>⑧ 客户端得到响应，可能是一个普通的HTML页面，也可以是XML或JSON字符串，还可以是一张图片或者一个PDF文件。</p>
<h3 id="大型网站在架构上应当考虑哪些问题？"><a href="#大型网站在架构上应当考虑哪些问题？" class="headerlink" title="大型网站在架构上应当考虑哪些问题？"></a>大型网站在架构上应当考虑哪些问题？</h3><p>答： </p>
<ul>
<li>分层：分层是处理任何复杂系统最常见的手段之一，将系统横向切分成若干个层面，每个层面只承担单一的职责，然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型（OSI/RM）和Internet的TCP/IP模型都是分层结构，大型网站的软件系统也可以使用分层的理念将其分为持久层（提供数据存储和访问服务）、业务层（处理业务逻辑，系统中最核心的部分）和表示层（系统交互、视图展示）。需要指出的是：（1）分层是逻辑上的划分，在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块，这样可以使用更多的计算资源来应对用户的并发访问；（2）层与层之间应当有清晰的边界，这样分层才有意义，才更利于软件的开发和维护。 </li>
<li>分割：分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开，形成高内聚低耦合的功能模块（单元）。在设计初期可以做一个粗粒度的分割，将网站分割为若干个功能模块，后期还可以进一步对每个模块进行细粒度的分割，这样一方面有助于软件的开发和维护，另一方面有助于分布式的部署，提供网站的并发处理能力和功能的扩展。 </li>
<li>分布式：除了上面提到的内容，网站的静态资源（JavaScript、CSS、图片等）也可以采用独立分布式部署并采用独立的域名，这样可以减轻应用服务器的负载压力，也使得浏览器对资源的加载更快。数据的存取也应该是分布式的，传统的商业级关系型数据库产品基本上都支持分布式部署，而新生的NoSQL产品几乎都是分布式的。当然，网站后台的业务处理也要使用分布式技术，例如查询索引的构建、数据分析等，这些业务计算规模庞大，可以使用Hadoop以及MapReduce分布式计算框架来处理。 </li>
<li>集群：集群使得有更多的服务器提供相同的服务，可以更好的提供对并发的支持。 </li>
<li>缓存：所谓缓存就是用空间换取时间的技术，将数据尽可能放在距离计算最近的位置。使用缓存是网站优化的第一定律。我们通常说的CDN、反向代理、热点数据都是对缓存技术的使用。 </li>
<li>异步：异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的生产者消费者模式，二者之间没有直接的调用关系，只要保持数据结构不变，彼此功能实现可以随意变化而不互相影响，这对网站的扩展非常有利。使用异步处理还可以提高系统可用性，加快网站的响应速度（用Ajax加载数据就是一种异步技术），同时还可以起到削峰作用（应对瞬时高并发）。”；能推迟处理的都要推迟处理”是网站优化的第二定律，而异步是践行网站优化第二定律的重要手段。 </li>
<li>冗余：各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机时还能保证网站可以正常工作，同时也提供了灾难恢复的可能性。冗余是网站高可用性的重要保证。</li>
</ul>
<h3 id="你用过的网站前端优化的技术有哪些？"><a href="#你用过的网站前端优化的技术有哪些？" class="headerlink" title="你用过的网站前端优化的技术有哪些？"></a>你用过的网站前端优化的技术有哪些？</h3><p>答：<br>① 浏览器访问优化： </p>
<ul>
<li>减少HTTP请求数量：合并CSS、合并JavaScript、合并图片（CSS Sprite） </li>
<li>使用浏览器缓存：通过设置HTTP响应头中的Cache-Control和Expires属性，将CSS、JavaScript、图片等在浏览器中缓存，当这些静态资源需要更新时，可以更新HTML文件中的引用来让浏览器重新请求新的资源 </li>
<li>启用压缩 </li>
<li>CSS前置，JavaScript后置 </li>
<li>减少Cookie传输<br>② CDN加速：CDN（Content Distribute Network）的本质仍然是缓存，将数据缓存在离用户最近的地方，CDN通常部署在网络运营商的机房，不仅可以提升响应速度，还可以减少应用服务器的压力。当然，CDN缓存的通常都是静态资源。<br>③ 反向代理：反向代理相当于应用服务器的一个门面，可以保护网站的安全性，也可以实现负载均衡的功能，当然最重要的是它缓存了用户访问的热点资源，可以直接从反向代理将某些内容返回给用户浏览器。</li>
</ul>
<h3 id="你使用过的应用服务器优化技术有哪些？"><a href="#你使用过的应用服务器优化技术有哪些？" class="headerlink" title="你使用过的应用服务器优化技术有哪些？"></a>你使用过的应用服务器优化技术有哪些？</h3><p>答：<br>① 分布式缓存：缓存的本质就是内存中的哈希表，如果设计一个优质的哈希函数，那么理论上哈希表读写的渐近时间复杂度为O(1)。缓存主要用来存放那些读写比很高、变化很少的数据，这样应用程序读取数据时先到缓存中读取，如果没有或者数据已经失效再去访问数据库或文件系统，并根据拟定的规则将数据写入缓存。对网站数据的访问也符合二八定律（Pareto分布，幂律分布），即80%的访问都集中在20%的数据上，如果能够将这20%的数据缓存起来，那么系统的性能将得到显著的改善。当然，使用缓存需要解决以下几个问题： </p>
<ul>
<li>频繁修改的数据； </li>
<li>数据不一致与脏读； </li>
<li>缓存雪崩（可以采用分布式缓存服务器集群加以解决，memcached是广泛采用的解决方案）； </li>
<li>缓存预热； </li>
<li>缓存穿透（恶意持续请求不存在的数据）。<br>② 异步操作：可以使用消息队列将调用异步化，通过异步处理将短时间高并发产生的事件消息存储在消息队列中，从而起到削峰作用。电商网站在进行促销活动时，可以将用户的订单请求存入消息队列，这样可以抵御大量的并发订单请求对系统和数据库的冲击。目前，绝大多数的电商网站即便不进行促销活动，订单系统都采用了消息队列来处理。<br>③ 使用集群。<br>④ 代码优化： </li>
<li>多线程：基于Java的Web开发基本上都通过多线程的方式响应用户的并发请求，使用多线程技术在编程上要解决线程安全问题，主要可以考虑以下几个方面：A. 将对象设计为无状态对象（这和面向对象的编程观点是矛盾的，在面向对象的世界中被视为不良设计），这样就不会存在并发访问时对象状态不一致的问题。B. 在方法内部创建对象，这样对象由进入方法的线程创建，不会出现多个线程访问同一对象的问题。使用ThreadLocal将对象与线程绑定也是很好的做法，这一点在前面已经探讨过了。C. 对资源进行并发访问时应当使用合理的锁机制。 </li>
<li>非阻塞I/O： 使用单线程和非阻塞I/O是目前公认的比多线程的方式更能充分发挥服务器性能的应用模式，基于Node.js构建的服务器就采用了这样的方式。Java在JDK 1.4中就引入了NIO（Non-blocking I/O）,在Servlet 3规范中又引入了异步Servlet的概念，这些都为在服务器端采用非阻塞I/O提供了必要的基础。 </li>
<li>资源复用：资源复用主要有两种方式，一是单例，二是对象池，我们使用的数据库连接池、线程池都是对象池化技术，这是典型的用空间换取时间的策略，另一方面也实现对资源的复用，从而避免了不必要的创建和释放资源所带来的开销。</li>
</ul>
<h3 id="什么是XSS攻击？什么是SQL注入攻击？什么是CSRF攻击？"><a href="#什么是XSS攻击？什么是SQL注入攻击？什么是CSRF攻击？" class="headerlink" title="什么是XSS攻击？什么是SQL注入攻击？什么是CSRF攻击？"></a>什么是XSS攻击？什么是SQL注入攻击？什么是CSRF攻击？</h3><p> 答： </p>
<ul>
<li>XSS（Cross Site Script，跨站脚本攻击）是向网页中注入恶意脚本在用户浏览网页时在用户浏览器中执行恶意脚本的攻击方式。跨站脚本攻击分有两种形式：反射型攻击（诱使用户点击一个嵌入恶意脚本的链接以达到攻击的目标，目前有很多攻击者利用论坛、微博发布含有恶意脚本的URL就属于这种方式）和持久型攻击（将恶意脚本提交到被攻击网站的数据库中，用户浏览网页时，恶意脚本从数据库中被加载到页面执行，QQ邮箱的早期版本就曾经被利用作为持久型跨站脚本攻击的平台）。XSS虽然不是什么新鲜玩意，但是攻击的手法却不断翻新，防范XSS主要有两方面：消毒（对危险字符进行转义）和HttpOnly（防范XSS攻击者窃取Cookie数据）。 </li>
<li>SQL注入攻击是注入攻击最常见的形式（此外还有OS注入攻击（Struts 2的高危漏洞就是通过OGNL实施OS注入攻击导致的）），当服务器使用请求参数构造SQL语句时，恶意的SQL被嵌入到SQL中交给数据库执行。SQL注入攻击需要攻击者对数据库结构有所了解才能进行，攻击者想要获得表结构有多种方式：（1）如果使用开源系统搭建网站，数据库结构也是公开的（目前有很多现成的系统可以直接搭建论坛，电商网站，虽然方便快捷但是风险是必须要认真评估的）；（2）错误回显（如果将服务器的错误信息直接显示在页面上，攻击者可以通过非法参数引发页面错误从而通过错误信息了解数据库结构，Web应用应当设置友好的错误页，一方面符合最小惊讶原则，一方面屏蔽掉可能给系统带来危险的错误回显信息）；（3）盲注。防范SQL注入攻击也可以采用消毒的方式，通过正则表达式对请求参数进行验证，此外，参数绑定也是很好的手段，这样恶意的SQL会被当做SQL的参数而不是命令被执行，JDBC中的PreparedStatement就是支持参数绑定的语句对象，从性能和安全性上都明显优于Statement。 </li>
<li>CSRF攻击（Cross Site Request Forgery，跨站请求伪造）是攻击者通过跨站请求，以合法的用户身份进行非法操作（如转账或发帖等）。CSRF的原理是利用浏览器的Cookie或服务器的Session，盗取用户身份，其原理如下图所示。防范CSRF的主要手段是识别请求者的身份，主要有以下几种方式：（1）在表单中添加令牌（token）；（2）验证码；（3）检查请求头中的Referer（前面提到防图片盗链接也是用的这种方式）。令牌和验证都具有一次消费性的特征，因此在原理上一致的，但是验证码是一种糟糕的用户体验，不是必要的情况下不要轻易使用验证码，目前很多网站的做法是如果在短时间内多次提交一个表单未获得成功后才要求提供验证码，这样会获得较好的用户体验。</li>
</ul>
<p>补充：防火墙的架设是Web安全的重要保障，ModSecurity是开源的Web防火墙中的佼佼者。企业级防火墙的架设应当有两级防火墙，Web服务器和部分应用服务器可以架设在两级防火墙之间的DMZ，而数据和资源服务器应当架设在第二级防火墙之后。</p>
<h3 id="什么是领域模型-domain-model-？贫血模型-anaemic-domain-model-和充血模型-rich-domain-model-有什么区别？"><a href="#什么是领域模型-domain-model-？贫血模型-anaemic-domain-model-和充血模型-rich-domain-model-有什么区别？" class="headerlink" title="什么是领域模型(domain model)？贫血模型(anaemic domain model)和充血模型(rich domain model)有什么区别？"></a>什么是领域模型(domain model)？贫血模型(anaemic domain model)和充血模型(rich domain model)有什么区别？</h3><p>答：领域模型是领域内的概念类或现实世界中对象的可视化表示，又称为概念模型或分析对象模型，它专注于分析问题领域本身，发掘重要的业务领域概念，并建立业务领域概念之间的关系。贫血模型是指使用的领域对象中只有setter和getter方法（POJO），所有的业务逻辑都不包含在领域对象中而是放在业务逻辑层。有人将我们这里说的贫血模型进一步划分成失血模型（领域对象完全没有业务逻辑）和贫血模型（领域对象有少量的业务逻辑），我们这里就不对此加以区分了。充血模型将大多数业务逻辑和持久化放在领域对象中，业务逻辑（业务门面）只是完成对业务逻辑的封装、事务和权限等的处理。下面两张图分别展示了贫血模型和充血模型的分层架构。</p>
<p>贫血模型 </p>
<p>充血模型 </p>
<p>贫血模型下组织领域逻辑通常使用事务脚本模式，让每个过程对应用户可能要做的一个动作，每个动作由一个过程来驱动。也就是说在设计业务逻辑接口的时候，每个方法对应着用户的一个操作，这种模式有以下几个有点： </p>
<ul>
<li>它是一个大多数开发者都能够理解的简单过程模型（适合国内的绝大多数开发者）。 </li>
<li>它能够与一个使用行数据入口或表数据入口的简单数据访问层很好的协作。 </li>
<li>事务边界的显而易见，一个事务开始于脚本的开始，终止于脚本的结束，很容易通过代理（或切面）实现声明式事务。<br>然而，事务脚本模式的缺点也是很多的，随着领域逻辑复杂性的增加，系统的复杂性将迅速增加，程序结构将变得极度混乱。开源中国社区上有一篇很好的译文《贫血领域模型是如何导致糟糕的软件产生》对这个问题做了比较细致的阐述。</li>
</ul>
<h3 id="谈一谈测试驱动开发（TDD）的好处以及你的理解。"><a href="#谈一谈测试驱动开发（TDD）的好处以及你的理解。" class="headerlink" title="谈一谈测试驱动开发（TDD）的好处以及你的理解。"></a>谈一谈测试驱动开发（TDD）的好处以及你的理解。</h3><p>答：TDD是指在编写真正的功能实现代码之前先写测试代码，然后根据需要重构实现代码。在JUnit的作者Kent Beck的大作《测试驱动开发：实战与模式解析》（Test-Driven Development: by Example）一书中有这么一段内容：“消除恐惧和不确定性是编写测试驱动代码的重要原因”。因为编写代码时的恐惧会让你小心试探，让你回避沟通，让你羞于得到反馈，让你变得焦躁不安，而TDD是消除恐惧、让Java开发者更加自信更加乐于沟通的重要手段。TDD会带来的好处可能不会马上呈现，但是你在某个时候一定会发现，这些好处包括： </p>
<ul>
<li>更清晰的代码 — 只写需要的代码 </li>
<li>更好的设计 </li>
<li>更出色的灵活性 — 鼓励程序员面向接口编程 </li>
<li>更快速的反馈 — 不会到系统上线时才知道bug的存在</li>
</ul>
<p>补充：敏捷软件开发的概念已经有很多年了，而且也部分的改变了软件开发这个行业，TDD也是敏捷开发所倡导的。</p>
<p>TDD可以在多个层级上应用，包括单元测试（测试一个类中的代码）、集成测试（测试类之间的交互）、系统测试（测试运行的系统）和系统集成测试（测试运行的系统包括使用的第三方组件）。TDD的实施步骤是：红（失败测试）- 绿（通过测试） - 重构。关于实施TDD的详细步骤请参考另一篇文章《测试驱动开发之初窥门径》。<br>在使用TDD开发时，经常会遇到需要被测对象需要依赖其他子系统的情况，但是你希望将测试代码跟依赖项隔离，以保证测试代码仅仅针对当前被测对象或方法展开，这时候你需要的是测试替身。测试替身可以分为四类： </p>
<ul>
<li>虚设替身：只传递但是不会使用到的对象，一般用于填充方法的参数列表 </li>
<li>存根替身：总是返回相同的预设响应，其中可能包括一些虚设状态 </li>
<li>伪装替身：可以取代真实版本的可用版本（比真实版本还是会差很多） </li>
<li>模拟替身：可以表示一系列期望值的对象，并且可以提供预设响应<br>Java世界中实现模拟替身的第三方工具非常多，包括EasyMock、Mockito、jMock等。</li>
</ul>
<h3 id="什么是可变参数？"><a href="#什么是可变参数？" class="headerlink" title="什么是可变参数？"></a>什么是可变参数？</h3><p>可变参数允许调用参数数量不同的方法。请看下面例子中的求和方法。此方法可以调用1个int参数，或2个int参数，或多个int参数。</p>
<p> //int(type) followed … (three dot’s) is syntax of a variable argument. </p>
<p>​    public int sum(int… numbers) {</p>
<p>​        //inside the method a variable argument is similar to an array.</p>
<p>​        //number can be treated as if it is declared as int[] numbers;</p>
<p>​        int sum = 0;</p>
<p>​        for (int number: numbers) {</p>
<p>​            sum += number;</p>
<p>​        }</p>
<p>​        return sum;</p>
<p>​    }</p>
<p>​    public static void main(String[] args) {</p>
<p>​        VariableArgumentExamples example = new VariableArgumentExamples();</p>
<p>​        //3 Arguments</p>
<p>​        System.out.println(example.sum(1, 4, 5));//10</p>
<p>​        //4 Arguments</p>
<p>​        System.out.println(example.sum(1, 4, 5, 20));//30</p>
<p>​        //0 Arguments</p>
<p>​        System.out.println(example.sum());//0</p>
<p>}</p>
<h3 id="断言的用途？"><a href="#断言的用途？" class="headerlink" title="断言的用途？"></a>断言的用途？</h3><p>断言是在Java 1.4中引入的。它能让你验证假设。如果断言失败（即返回false），就会抛出AssertionError（如果启用断言）。基本断言如下所示。</p>
<p>private int computerSimpleInterest(int principal,float interest,int years){</p>
<p>​    assert(principal&gt;0);</p>
<p>​    return 100;</p>
<p>}</p>
<h3 id="什么时候使用断言？"><a href="#什么时候使用断言？" class="headerlink" title="什么时候使用断言？"></a>什么时候使用断言？</h3><p>断言不应该用于验证输入数据到一个public方法或命令行参数。IllegalArgumentException会是一个更好的选择。在public方法中，只用断言来检查它们根本不应该发生的情况。</p>
<h3 id="什么是初始化数据块？"><a href="#什么是初始化数据块？" class="headerlink" title="什么是初始化数据块？"></a>什么是初始化数据块？</h3><p>初始化数据块——当创建对象或加载类时运行的代码。</p>
<p>有两种类型的初始化数据块：</p>
<p>静态初始化器：加载类时运行的的代码</p>
<p>实例初始化器：创建新对象时运行的代码</p>
<h3 id="什么是静态初始化器？"><a href="#什么是静态初始化器？" class="headerlink" title="什么是静态初始化器？"></a>什么是静态初始化器？</h3><p>请看下面的例子：static{ 和 }之间的代码被称为静态初始化器。它只有在第一次加载类时运行。只有静态变量才可以在静态初始化器中进行访问。虽然创建了三个实例，但静态初始化器只运行一次。</p>
<p>/**</p>
<ul>
<li><p>Java学习交流QQ群：589809992 我们一起学Java！</p>
<p>*/</p>
</li>
</ul>
<p>public class InitializerExamples {</p>
<p>​    static int count;</p>
<p>​    int i;</p>
<p>​    static{</p>
<p>​        //This is a static initializers. Run only when Class is first loaded.</p>
<p>​        //Only static variables can be accessed</p>
<p>​        System.out.println(“Static Initializer”);</p>
<p>​        //i = 6;//COMPILER ERROR</p>
<p>​        System.out.println(“Count when Static Initializer is run is “ + count);</p>
<p>​    }</p>
<p>​    public static void main(String[] args) {</p>
<p>​        InitializerExamples example = new InitializerExamples();</p>
<p>​        InitializerExamples example2 = new InitializerExamples();</p>
<p>​        InitializerExamples example3 = new InitializerExamples();</p>
<p>​    }</p>
<p>}</p>
<p>示例输出</p>
<p>Static Initializer</p>
<p>Count when Static Initializer is run is 0.</p>
<h3 id="什么是实例初始化块？"><a href="#什么是实例初始化块？" class="headerlink" title="什么是实例初始化块？"></a>什么是实例初始化块？</h3><p>让我们来看一个例子：每次创建类的实例时，实例初始化器中的代码都会运行。</p>
<p>/**</p>
<ul>
<li><p>Java学习交流QQ群：589809992 我们一起学Java！</p>
<p>*/</p>
</li>
</ul>
<p>public class InitializerExamples {</p>
<p>​    static int count;</p>
<p>​    int i;</p>
<p>​    {</p>
<p>​        //This is an instance initializers. Run every time an object is created.</p>
<p>​        //static and instance variables can be accessed</p>
<p>​        System.out.println(“Instance Initializer”);</p>
<p>​        i = 6;</p>
<p>​        count = count + 1;</p>
<p>​        System.out.println(“Count when Instance Initializer is run is “ + count);</p>
<p>​    }</p>
<p>​    public static void main(String[] args) {</p>
<p>​        InitializerExamples example = new InitializerExamples();</p>
<p>​        InitializerExamples example1 = new InitializerExamples();</p>
<p>​        InitializerExamples example2 = new InitializerExamples();</p>
<p>​    }</p>
<p>}</p>
<p>示例输出</p>
<p>Instance Initializer</p>
<p>​      Count when Instance Initializer is run is 1</p>
<p>​      Instance Initializer</p>
<p>​      Count when Instance Initializer is run is 2</p>
<p>​      Instance Initializer</p>
<p>​      Count when Instance Initializer is run is 3</p>
<h3 id="什么是正则表达式？"><a href="#什么是正则表达式？" class="headerlink" title="什么是正则表达式？"></a>什么是正则表达式？</h3><p>正则表达式能让解析、扫描和分割字符串变得非常容易。Java中常用的正则表达式——Patter，Matcher和Scanner类。</p>
<h3 id="什么是令牌化？"><a href="#什么是令牌化？" class="headerlink" title="什么是令牌化？"></a>什么是令牌化？</h3><p>令牌化是指在分隔符的基础上将一个字符串分割为若干个子字符串。例如，分隔符；分割字符串ac;bd;def;e为四个子字符串ac，bd，def和e。</p>
<p>分隔符自身也可以是一个常见正则表达式。</p>
<p>String.split(regex)函数将regex作为参数。</p>
<h3 id="给出令牌化的例子？"><a href="#给出令牌化的例子？" class="headerlink" title="给出令牌化的例子？"></a>给出令牌化的例子？</h3><p>private static void tokenize(String string,String regex) {</p>
<p>​    String[] tokens = string.split(regex);</p>
<p>​    System.out.println(Arrays.toString(tokens));</p>
<p>}</p>
<p>tokenize(“ac;bd;def;e”,”;”);//[ac, bd, def, e]</p>
<h3 id="如何使用扫描器类（Scanner-Class）令牌化？"><a href="#如何使用扫描器类（Scanner-Class）令牌化？" class="headerlink" title="如何使用扫描器类（Scanner Class）令牌化？"></a>如何使用扫描器类（Scanner Class）令牌化？</h3><p>private static void tokenizeUsingScanner(String string,String regex) {</p>
<p>​    Scanner scanner = new Scanner(string);</p>
<p>​    scanner.useDelimiter(regex);</p>
<p>​    List<string> matches = new ArrayList<string>();</string></string></p>
<p>​    while(scanner.hasNext()){</p>
<p>​        matches.add(scanner.next());</p>
<p>​    }</p>
<p>​    System.out.println(matches);</p>
<p>}</p>
<p>tokenizeUsingScanner(“ac;bd;def;e”,”;”);//[ac, bd, def, e]</p>
<h3 id="如何添加小时-hour-到一个日期对象（Date-Objects）？"><a href="#如何添加小时-hour-到一个日期对象（Date-Objects）？" class="headerlink" title="如何添加小时(hour)到一个日期对象（Date Objects）？"></a>如何添加小时(hour)到一个日期对象（Date Objects）？</h3><p>现在，让我们如何看看添加小时到一个date对象。所有在date上的日期操作都需要通过添加毫秒到date才能完成。例如，如果我们想增加6个小时，那么我们需要将6小时换算成毫秒。6小时= 6  60  60 * 1000毫秒。请看以下的例子。</p>
<p>Date date = new Date();</p>
<p>//Increase time by 6 hrs</p>
<p>date.setTime(date.getTime() + 6 <em> 60 </em> 60 * 1000);</p>
<p>System.out.println(date);</p>
<p>//Decrease time by 6 hrs</p>
<p>date = new Date();</p>
<p>date.setTime(date.getTime() - 6 <em> 60 </em> 60 * 1000);</p>
<p>System.out.println(date);</p>
<h3 id="如何格式化日期对象？"><a href="#如何格式化日期对象？" class="headerlink" title="如何格式化日期对象？"></a>如何格式化日期对象？</h3><p>格式化日期需要使用DateFormat类完成。让我们看几个例子。</p>
<p>//Formatting Dates</p>
<p>System.out.println(DateFormat.getInstance().format(</p>
<p>​        date));//10/16/12 5:18 AM</p>
<p>带有区域设置的格式化日期如下所示：</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.FULL, new Locale(“it”, “IT”))</p>
<p>​        .format(date));//marted“ 16 ottobre 2012</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.FULL, Locale.ITALIAN)</p>
<p>​        .format(date));//marted“ 16 ottobre 2012</p>
<p>//This uses default locale US</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.FULL).format(date));//Tuesday, October 16, 2012</p>
<p>System.out.println(DateFormat.getDateInstance()</p>
<p>​        .format(date));//Oct 16, 2012</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.SHORT).format(date));//10/16/12</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.MEDIUM).format(date));//Oct 16, 2012</p>
<p>System.out.println(DateFormat.getDateInstance(</p>
<p>​        DateFormat.LONG).format(date));//October 16, 2012</p>
<h3 id="Java中日历类（Calendar-Class）的用途？"><a href="#Java中日历类（Calendar-Class）的用途？" class="headerlink" title="Java中日历类（Calendar Class）的用途？"></a>Java中日历类（Calendar Class）的用途？</h3><p>Calendar类在Java中用于处理日期。Calendar类提供了增加和减少天数、月数和年数的简便方法。它还提供了很多与日期有关的细节（这一年的哪一天？哪一周？等等）</p>
<h3 id="如何在Java中获取日历类（Calendar-Class）的实例？"><a href="#如何在Java中获取日历类（Calendar-Class）的实例？" class="headerlink" title="如何在Java中获取日历类（Calendar Class）的实例？"></a>如何在Java中获取日历类（Calendar Class）的实例？</h3><p>Calendar类不能通过使用new Calendar创建。得到Calendar类实例的最好办法是在Calendar中使用getInstance() static方法。</p>
<p>//Calendar calendar = new Calendar(); //COMPILER ERROR</p>
<p>Calendar calendar = Calendar.getInstance();</p>
<h3 id="解释一些日历类（Calendar-Class）中的重要方法？"><a href="#解释一些日历类（Calendar-Class）中的重要方法？" class="headerlink" title="解释一些日历类（Calendar Class）中的重要方法？"></a>解释一些日历类（Calendar Class）中的重要方法？</h3><p>在Calendar对象上设置日（day），月（month）或年（year）不难。对Day，Month或Year调用恰当Constant的set方法。下一个参数就是值。</p>
<p>calendar.set(Calendar.DATE, 24);</p>
<p>calendar.set(Calendar.MONTH, 8);//8 - September</p>
<p>calendar.set(Calendar.YEAR, 2010);</p>
<p>calendar get方法</p>
<p>要获取一个特定日期的信息——2010年9月24日。我们可以使用calendar get方法。已被传递的参数表示我们希望从calendar中获得的值—— 天或月或年或……你可以从calendar获取的值举例如下：</p>
<p>System.out.println(calendar.get(Calendar.YEAR));//2010</p>
<p>System.out.println(calendar.get(Calendar.MONTH));//8</p>
<p>System.out.println(calendar.get(Calendar.DATE));//24</p>
<p>System.out.println(calendar.get(Calendar.WEEK_OF_MONTH));//4</p>
<p>System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));//39</p>
<p>System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//267</p>
<p>System.out.println(calendar.getFirstDayOfWeek());//1 -&gt; Calendar.SUNDAY</p>
<h3 id="数字格式化类（Number-Format-Class）的用途？"><a href="#数字格式化类（Number-Format-Class）的用途？" class="headerlink" title="数字格式化类（Number Format Class）的用途？"></a>数字格式化类（Number Format Class）的用途？</h3><p>数字格式用于格式化数字到不同的区域和不同格式中。</p>
<p>使用默认语言环境的数字格式</p>
<p>System.out.println(NumberFormat.getInstance().format(321.24f));//321.24</p>
<p>使用区域设置的数字格式</p>
<p>使用荷兰语言环境格式化数字：</p>
<p>System.out.println(NumberFormat.getInstance(new Locale(“nl”)).format(4032.3f));//4.032,3</p>
<p>使用德国语言环境格式化数字：</p>
<p>System.out.println(NumberFormat.getInstance(Locale.GERMANY).format(4032.3f));//4.032,3</p>
<p>使用默认语言环境格式化货币</p>
<p>System.out.println(NumberFormat.getCurrencyInstance().format(40324.31f));//$40,324.31</p>
<p>使用区域设置格式化货币</p>
<p>使用荷兰语言环境格式化货币：</p>
<p>System.out.println(NumberFormat.getCurrencyInstance(new Locale(“nl”)).format(40324.31f));/</p>
<h3 id="后台从前端页面获取到表单数据的方法？请具体到细节，如Servlet如何接收？SpringMVC怎么接收或Structs2？"><a href="#后台从前端页面获取到表单数据的方法？请具体到细节，如Servlet如何接收？SpringMVC怎么接收或Structs2？" class="headerlink" title="后台从前端页面获取到表单数据的方法？请具体到细节，如Servlet如何接收？SpringMVC怎么接收或Structs2？"></a>后台从前端页面获取到表单数据的方法？请具体到细节，如Servlet如何接收？SpringMVC怎么接收或Structs2？</h3><h3 id="Spring-中-IOC-和-DI-的区别以及关系是什么、AOP是怎么实现的？"><a href="#Spring-中-IOC-和-DI-的区别以及关系是什么、AOP是怎么实现的？" class="headerlink" title="Spring 中 IOC 和 DI 的区别以及关系是什么、AOP是怎么实现的？"></a>Spring 中 IOC 和 DI 的区别以及关系是什么、AOP是怎么实现的？</h3>

    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-Struts-框架/">
        Java 面试题系列篇-struts 框架
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="Struts-框架"><a href="#Struts-框架" class="headerlink" title="Struts 框架"></a>Struts 框架</h2><h3 id="Struts2-中action-是多实例还是单实例的？为什么？"><a href="#Struts2-中action-是多实例还是单实例的？为什么？" class="headerlink" title="Struts2 中action 是多实例还是单实例的？为什么？"></a>Struts2 中action 是多实例还是单实例的？为什么？</h3><h3 id="Struts2-中的拦截器如何配置？"><a href="#Struts2-中的拦截器如何配置？" class="headerlink" title="Struts2 中的拦截器如何配置？"></a>Struts2 中的拦截器如何配置？</h3><h3 id="SSH-框架或-SSM-框架整合思想是什么？关键-jar-包是哪几个？请具体阐述-jar-包的作用？"><a href="#SSH-框架或-SSM-框架整合思想是什么？关键-jar-包是哪几个？请具体阐述-jar-包的作用？" class="headerlink" title="SSH 框架或 SSM 框架整合思想是什么？关键 jar 包是哪几个？请具体阐述 jar 包的作用？"></a>SSH 框架或 SSM 框架整合思想是什么？关键 jar 包是哪几个？请具体阐述 jar 包的作用？</h3><h3 id="页面数据分页如何实现？"><a href="#页面数据分页如何实现？" class="headerlink" title="页面数据分页如何实现？"></a>页面数据分页如何实现？</h3>

    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-Spring-框架/">
        Java 面试题系列篇-Spring 框架
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="Spring-框架"><a href="#Spring-框架" class="headerlink" title="Spring 框架"></a>Spring 框架</h2><h3 id="【Spring】使用Spring框架的好处是什么？"><a href="#【Spring】使用Spring框架的好处是什么？" class="headerlink" title="【Spring】使用Spring框架的好处是什么？"></a>【Spring】使用Spring框架的好处是什么？</h3><p>轻量：Spring 是轻量的，基本的版本大约2MB</p>
<p>控制反转：Spring通过控制反转实现了松散耦合，对象们给出它们的依赖，而不是创建或查找依赖的对象们</p>
<p>面向切面的编程(AOP)：Spring支持面向切面的编程，并且把应用业务逻辑和系统服务分开</p>
<p>容器：Spring 包含并管理应用中对象的生命周期和配置</p>
<p>MVC框架：Spring的WEB框架是个精心设计的框架，是Web框架的一个很好的替代品</p>
<p>事务管理：Spring 提供一个持续的事务管理接口，可以扩展到上至本地事务下至全局事务（JTA）</p>
<p>异常处理：Spring 提供方便的API把具体技术相关的异常（比如由JDBC，Hibernate or JDO抛出的）转化为一致的unchecked 异常</p>
<h3 id="什么是IoC和DI？DI是如何实现的？"><a href="#什么是IoC和DI？DI是如何实现的？" class="headerlink" title="什么是IoC和DI？DI是如何实现的？"></a>什么是IoC和DI？DI是如何实现的？</h3><p>答：IoC叫控制反转，是Inversion of Control的缩写，DI（Dependency Injection）叫依赖注入，是对IoC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器，通过容器来实现对象组件的装配和管理。所谓的”控制反转”就是对组件对象控制权的转移，从程序代码本身转移到了外部容器，由容器来创建对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 - “Don’t call me, we will call you”。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责，查找资源的逻辑应该从应用组件的代码中抽取出来，交给容器来完成。DI是对IoC更准确的描述，即组件之间的依赖关系由容器在运行期决定，形象的来说，即由容器动态的将某种依赖关系注入到组件之中。</p>
<p>举个例子：一个类A需要用到接口B中的方法，那么就需要为类A和接口B建立关联或依赖关系，最原始的方法是在类A中创建一个接口B的实现类C的实例，但这种方法需要开发人员自行维护二者的依赖关系，也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系，则只需要在类A中定义好用于关联接口B的方法（构造器或setter方法），将类A和接口B的实现类C放入容器中，通过对容器的配置来实现二者的关联。</p>
<p>依赖注入可以通过setter方法注入（设值注入）、构造器注入和接口注入三种方式来实现，Spring支持setter注入和构造器注入，通常使用构造器注入来注入必须的依赖关系，对于可选的依赖关系，则setter注入是更好的选择，setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。</p>
<h3 id="Spring中Bean的作用域有哪些？"><a href="#Spring中Bean的作用域有哪些？" class="headerlink" title="Spring中Bean的作用域有哪些？"></a>Spring中Bean的作用域有哪些？</h3><p>答：在Spring的早期版本中，仅有两个作用域：singleton和prototype，前者表示Bean以单例的方式存在；后者表示每次从容器中调用Bean时，都会返回一个新的实例，prototype通常翻译为原型。</p>
<p>补充：设计模式中的创建型模式中也有一个原型模式，原型模式也是一个常用的模式，例如做一个室内设计软件，所有的素材都在工具箱中，而每次从工具箱中取出的都是素材对象的一个原型，可以通过对象克隆来实现原型模式。</p>
<p>Spring 2.x中针对WebApplicationContext新增了3个作用域，分别是：request（每次HTTP请求都会创建一个新的Bean）、session（同一个HttpSession共享同一个Bean，不同的HttpSession使用不同的Bean）和globalSession（同一个全局Session共享一个Bean）。</p>
<p>说明：单例模式和原型模式都是重要的设计模式。一般情况下，无状态或状态不可变的类适合使用单例模式。在传统开发中，由于DAO持有Connection这个非线程安全对象因而没有使用单例模式；但在Spring环境下，所有DAO类对可以采用单例模式，因为Spring利用AOP和Java API中的ThreadLocal对非线程安全的对象进行了特殊处理。</p>
<p>ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal，顾名思义是线程的一个本地化对象，当工作于多线程中的对象使用ThreadLocal维护变量时，ThreadLocal为每个使用该变量的线程分配一个独立的变量副本，所以每一个线程都可以独立的改变自己的副本，而不影响其他线程所对应的副本。从线程的角度看，这个变量就像是线程的本地变量。</p>
<p>ThreadLocal类非常简单好用，只有四个方法，能用上的也就是下面三个方法： </p>
<ul>
<li>void set(T value)：设置当前线程的线程局部变量的值。 </li>
<li>T get()：获得当前线程所对应的线程局部变量的值。 </li>
<li>void remove()：删除当前线程中线程局部变量的值。</li>
</ul>
<p>ThreadLocal是如何做到为每一个线程维护一份独立的变量副本的呢？在ThreadLocal类中有一个Map，键为线程对象，值是其线程对应的变量的副本，自己要模拟实现一个ThreadLocal类其实并不困难，代码如下所示：</p>
<p>import java.util.Collections;</p>
<p>import java.util.HashMap;</p>
<p>import java.util.Map;</p>
<p>public class MyThreadLocal<t> {</t></p>
<p>​    private Map&lt;Thread, T&gt; map = Collections.synchronizedMap(new HashMap&lt;Thread, T&gt;());</p>
<p>​    public void set(T newValue) {</p>
<p>​        map.put(Thread.currentThread(), newValue);</p>
<p>​    }</p>
<p>​    public T get() {</p>
<p>​        return map.get(Thread.currentThread());</p>
<p>​    }</p>
<p>​    public void remove() {</p>
<p>​        map.remove(Thread.currentThread());</p>
<p>​    }</p>
<p>}</p>
<h3 id="解释一下什么叫AOP（面向切面编程）？"><a href="#解释一下什么叫AOP（面向切面编程）？" class="headerlink" title="解释一下什么叫AOP（面向切面编程）？"></a>解释一下什么叫AOP（面向切面编程）？</h3><p>答：AOP（Aspect-Oriented Programming）指一种程序设计范型，该范型以一种称为切面（aspect）的语言构造为基础，切面是一种新的模块化机制，用来描述分散在对象、类或方法中的横切关注点（crosscutting concern）。</p>
<h3 id="你是如何理解”横切关注”这个概念的？"><a href="#你是如何理解”横切关注”这个概念的？" class="headerlink" title="你是如何理解”横切关注”这个概念的？"></a>你是如何理解”横切关注”这个概念的？</h3><p>答：”横切关注”是会影响到整个应用程序的关注功能，它跟正常的业务逻辑是正交的，没有必然的联系，但是几乎所有的业务逻辑都会涉及到这些关注功能。通常，事务、日志、安全性等关注就是应用中的横切关注功能。</p>
<h3 id="你如何理解AOP中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？"><a href="#你如何理解AOP中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？" class="headerlink" title="你如何理解AOP中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？"></a>你如何理解AOP中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？</h3><p>答：<br>a. 连接点（Joinpoint）：程序执行的某个特定位置（如：某个方法调用前、调用后，方法抛出异常后）。一个类或一段程序代码拥有一些具有边界性质的特定点，这些代码中的特定点就是连接点。Spring仅支持方法的连接点。<br>b. 切点（Pointcut）：如果连接点相当于数据中的记录，那么切点相当于查询条件，一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件，找到对应的连接点。<br>c. 增强（Advice）：增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的，如：BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多资料上将增强译为“通知”，这明显是个词不达意的翻译，让很多程序员困惑了许久。</p>
<p>说明： Advice在国内的很多书面资料中都被翻译成”通知”，但是很显然这个翻译无法表达其本质，有少量的读物上将这个词翻译为”增强”，这个翻译是对Advice较为准确的诠释，我们通过AOP将横切关注功能加到原有的业务逻辑上，这就是对原有业务逻辑的一种增强，这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。</p>
<p>d. 引介（Introduction）：引介是一种特殊的增强，它为类添加一些属性和方法。这样，即使一个业务类原本没有实现某个接口，通过引介功能，可以动态的未该业务类添加接口的实现逻辑，让业务类成为这个接口的实现类。<br>e. 织入（Weaving）：织入是将增强添加到目标类具体连接点上的过程，AOP有三种织入方式：①编译期织入：需要特殊的Java编译期（例如AspectJ的ajc）；②装载期织入：要求使用特殊的类加载器，在装载类的时候对类进行增强；③运行时织入：在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入，而AspectJ采用了编译期织入和装载期织入的方式。<br>f. 切面（Aspect）：切面是由切点和增强（引介）组成的，它包括了对横切关注功能的定义，也包括了对连接点的定义。</p>
<p>补充：代理模式是GoF提出的23种设计模式中最为经典的模式之一，代理模式是对象的结构模式，它给某一个对象提供一个代理对象，并由代理对象控制对原对象的引用。简单的说，代理对象可以完成比原对象更多的职责，当需要为原对象添加横切关注功能时，就可以使用原对象的代理对象。我们在打开Office系列的Word文档时，如果文档中有插图，当文档刚加载时，文档中的插图都只是一个虚框占位符，等用户真正翻到某页要查看该图片时，才会真正加载这张图，这其实就是对代理模式的使用，代替真正图片的虚框就是一个虚拟代理；Hibernate的load方法也是返回一个虚拟代理对象，等用户真正需要访问对象的属性时，才向数据库发出SQL语句获得真实对象。</p>
<p>下面用一个找枪手代考的例子演示代理模式的使用：</p>
<p>/**</p>
<ul>
<li>参考人员接口</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public interface Candidate {</p>
<p>​    /**</p>
<p>​     * 答题</p>
<p>​     */</p>
<p>​    public void answerTheQuestions();</p>
<p>}</p>
<p>/**</p>
<ul>
<li>懒学生</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public class LazyStudent implements Candidate {</p>
<p>​    private String name;        // 姓名</p>
<p>​    public LazyStudent(String name) {</p>
<p>​        this.name = name;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void answerTheQuestions() {</p>
<p>​        // 懒学生只能写出自己的名字不会答题</p>
<p>​        System.out.println(“姓名: “ + name);</p>
<p>​    }</p>
<p>}</p>
<p>/**</p>
<ul>
<li>枪手</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public class Gunman implements Candidate {</p>
<p>​    private Candidate target;   // 被代理对象</p>
<p>​    public Gunman(Candidate target) {</p>
<p>​        this.target = target;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void answerTheQuestions() {</p>
<p>​        // 枪手要写上代考的学生的姓名</p>
<p>​        target.answerTheQuestions();</p>
<p>​        // 枪手要帮助懒学生答题并交卷</p>
<p>​        System.out.println(“奋笔疾书正确答案”);</p>
<p>​        System.out.println(“交卷”);</p>
<p>​    }</p>
<p>}</p>
<p>public class ProxyTest1 {</p>
<p>​    public static void main(String[] args) {</p>
<p>​        Candidate c = new Gunman(new LazyStudent(“王小二”));</p>
<p>​        c.answerTheQuestions();</p>
<p>​    }</p>
<p>}</p>
<p>说明：从JDK 1.3开始，Java提供了动态代理技术，允许开发者在运行时创建接口的代理实例，主要包括Proxy类和InvocationHandler接口。下面的例子使用动态代理为ArrayList编写一个代理，在添加和删除元素时，在控制台打印添加或删除的元素以及ArrayList的大小：</p>
<p>import java.lang.reflect.InvocationHandler;</p>
<p>import java.lang.reflect.Method;</p>
<p>import java.util.List;</p>
<p>public class ListProxy<t> implements InvocationHandler {</t></p>
<p>​    private List<t> target;</t></p>
<p>​    public ListProxy(List<t> target) {</t></p>
<p>​        this.target = target;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public Object invoke(Object proxy, Method method, Object[] args)</p>
<p>​            throws Throwable {</p>
<p>​        Object retVal = null;</p>
<p>​        System.out.println(“[“ + method.getName() + “: “ + args[0] + “]”);</p>
<p>​        retVal = method.invoke(target, args);</p>
<p>​        System.out.println(“[size=” + target.size() + “]”);</p>
<p>​        return retVal;</p>
<p>​    }</p>
<p>}</p>
<p>import java.lang.reflect.Proxy;</p>
<p>import java.util.ArrayList;</p>
<p>import java.util.List;</p>
<p>public class ProxyTest2 {</p>
<p>​    @SuppressWarnings(“unchecked”)</p>
<p>​    public static void main(String[] args) {</p>
<p>​        List<string> list = new ArrayList<string>();</string></string></p>
<p>​        Class&lt;?&gt; clazz = list.getClass();</p>
<p>​        ListProxy<string> myProxy = new ListProxy<string>(list);</string></string></p>
<p>​        List<string> newList = (List<string>) </string></string></p>
<p>​                Proxy.newProxyInstance(clazz.getClassLoader(), </p>
<p>​                clazz.getInterfaces(), myProxy);</p>
<p>​        newList.add(“apple”);</p>
<p>​        newList.add(“banana”);</p>
<p>​        newList.add(“orange”);</p>
<p>​        newList.remove(“banana”);</p>
<p>​    }</p>
<p>}</p>
<p>说明：使用Java的动态代理有一个局限性就是代理的类必须要实现接口，虽然面向接口编程是每个优秀的Java程序都知道的规则，但现实往往不尽如人意，对于没有实现接口的类如何为其生成代理呢？继承！继承是最经典的扩展已有代码能力的手段，虽然继承常常被初学者滥用，但继承也常常被进阶的程序员忽视。CGLib采用非常底层的字节码生成技术，通过为一个类创建子类来生成代理，它弥补了Java动态代理的不足，因此Spring中动态代理和CGLib都是创建代理的重要手段，对于实现了接口的类就用动态代理为其生成代理类，而没有实现接口的类就用CGLib通过继承的方式为其创建代理。</p>
<h3 id="Spring中自动装配的方式有哪些？"><a href="#Spring中自动装配的方式有哪些？" class="headerlink" title="Spring中自动装配的方式有哪些？"></a>Spring中自动装配的方式有哪些？</h3><p> 答： </p>
<ul>
<li>no：不进行自动装配，手动设置Bean的依赖关系。 </li>
<li>byName：根据Bean的名字进行自动装配。 </li>
<li>byType：根据Bean的类型进行自动装配。 </li>
<li>constructor：类似于byType，不过是应用于构造器的参数，如果正好有一个Bean与构造器的参数类型相同则可以自动装配，否则会导致错误。 </li>
<li>autodetect：如果有默认的构造器，则通过constructor的方式进行自动装配，否则使用byType的方式进行自动装配。</li>
</ul>
<p>说明：自动装配没有自定义装配方式那么精确，而且不能自动装配简单属性（基本类型、字符串等），在使用时应注意。</p>
<h3 id="Spring中如何使用注解来配置Bean？有哪些相关的注解？"><a href="#Spring中如何使用注解来配置Bean？有哪些相关的注解？" class="headerlink" title="Spring中如何使用注解来配置Bean？有哪些相关的注解？"></a>Spring中如何使用注解来配置Bean？有哪些相关的注解？</h3><p>答：首先需要在Spring配置文件中增加如下配置：</p>
<p>&lt;context:component-scan base-package=”org.example”/&gt;</p>
<p>然后可以用@Component、@Controller、@Service、@Repository注解来标注需要由Spring IoC容器进行对象托管的类。这几个注解没有本质区别，只不过@Controller通常用于控制器，@Service通常用于业务逻辑类，@Repository通常用于仓储类（例如我们的DAO实现类），普通的类用@Component来标注。</p>
<h3 id="Spring支持的事务管理类型有哪些？你在项目中使用哪种方式？"><a href="#Spring支持的事务管理类型有哪些？你在项目中使用哪种方式？" class="headerlink" title="Spring支持的事务管理类型有哪些？你在项目中使用哪种方式？"></a>Spring支持的事务管理类型有哪些？你在项目中使用哪种方式？</h3><p>答：Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理，因为这种方式和应用程序的关联较少，因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理，尽管在灵活性方面它弱于编程式事务管理，因为编程式事务允许你通过代码控制业务。</p>
<p>事务分为全局事务和局部事务。全局事务由应用服务器管理，需要底层服务器JTA支持（如WebLogic、WildFly等）。局部事务和底层采用的持久化方案有关，例如使用JDBC进行持久化时，需要使用Connetion对象来操作事务；而采用Hibernate进行持久化时，需要使用Session对象来操作事务。</p>
<p>Spring提供了如下所示的事务管理器。</p>
<table>
<thead>
<tr>
<th>事务管理器实现类</th>
<th>目标对象</th>
</tr>
</thead>
<tbody>
<tr>
<td>DataSourceTransactionManager</td>
<td>注入DataSource</td>
</tr>
<tr>
<td>HibernateTransactionManager</td>
<td>注入SessionFactory</td>
</tr>
<tr>
<td>JdoTransactionManager</td>
<td>管理JDO事务</td>
</tr>
<tr>
<td>JtaTransactionManager</td>
<td>使用JTA管理事务</td>
</tr>
<tr>
<td>PersistenceBrokerTransactionManager</td>
<td>管理Apache的OJB事务</td>
</tr>
</tbody>
</table>
<p>这些事务的父接口都是PlatformTransactionManager。Spring的事务管理机制是一种典型的策略模式，PlatformTransactionManager代表事务管理接口，该接口定义了三个方法，该接口并不知道底层如何管理事务，但是它的实现类必须提供getTransaction()方法（开启事务）、commit()方法（提交事务）、rollback()方法（回滚事务）的多态实现，这样就可以用不同的实现类代表不同的事务管理策略。使用JTA全局事务策略时，需要底层应用服务器支持，而不同的应用服务器所提供的JTA全局事务可能存在细节上的差异，因此实际配置全局事务管理器是可能需要使用JtaTransactionManager的子类，如：WebLogicJtaTransactionManager（Oracle的WebLogic服务器提供）、UowJtaTransactionManager（IBM的WebSphere服务器提供）等。</p>
<p>编程式事务管理如下所示。</p>
<p>&lt;?xml version=”1.0” encoding=”UTF-8”?&gt;</p>
 <beans xmlns="http://www.springframework.org/schema/beans" ​ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

​     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><br><br><br><br>​     &lt;context:component-scan base-package=”com.jackfrued”/&gt;<br><br><br><br>​     <bean id="propertyConfig" ​ class="org.springframework.beans.factory.config.

  PropertyPlaceholderConfigurer"><br><br>​         <property name="location"><br><br>​             <value>jdbc.properties</value><br><br>​         </property><br><br>​     </bean><br><br><br><br>​     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><br><br>​         <property name="driverClassName"><br><br>​             <value>${db.driver}</value><br><br>​         </property><br><br>​         <property name="url"><br><br>​             <value>${db.url}</value><br><br>​         </property><br><br>​         <property name="username"><br><br>​             <value>${db.username}</value><br><br>​         </property><br><br>​         <property name="password"><br><br>​             <value>${db.password}</value><br><br>​         </property><br><br>​     </bean><br><br><br><br>​     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><br><br>​         <property name="dataSource"><br><br>​             <ref bean="dataSource"><br><br>​         </ref></property><br><br>​     </bean><br><br><br><br>​     <!-- JDBC事务管理器 --><br><br>​     <bean id="transactionManager" ​ class="org.springframework.jdbc.datasource.

​       DataSourceTransactionManager" 　scope="singleton"><br><br>​         <property name="dataSource"><br><br>​             <ref bean="dataSource"><br><br>​         </ref></property><br><br>​     </bean><br><br><br><br>​     <!-- 声明事务模板 --><br><br>​     <bean id="transactionTemplate" ​ class="org.springframework.transaction.support.

   TransactionTemplate"><br><br>​         <property name="transactionManager"><br><br>​             <ref bean="transactionManager"><br><br>​         </ref></property><br><br>​     </bean><br><br><br><br></beans>

<p>package com.jackfrued.dao.impl;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;</p>
<p>import org.springframework.jdbc.core.JdbcTemplate;</p>
<p>import com.jackfrued.dao.EmpDao;</p>
<p>import com.jackfrued.entity.Emp;</p>
<p>@Repository</p>
<p>public class EmpDaoImpl implements EmpDao {</p>
<p>​    @Autowired</p>
<p>​    private JdbcTemplate jdbcTemplate;</p>
<p>​    @Override</p>
<p>​    public boolean save(Emp emp) {</p>
<p>​        String sql = “insert into emp values (?,?,?)”;</p>
<p>​        return jdbcTemplate.update(sql, emp.getId(), emp.getName(), emp.getBirthday()) == 1;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.biz.impl;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;</p>
<p>import org.springframework.stereotype.Service;</p>
<p>import org.springframework.transaction.TransactionStatus;</p>
<p>import org.springframework.transaction.support.TransactionCallbackWithoutResult;</p>
<p>import org.springframework.transaction.support.TransactionTemplate;</p>
<p>import com.jackfrued.biz.EmpService;</p>
<p>import com.jackfrued.dao.EmpDao;</p>
<p>import com.jackfrued.entity.Emp;</p>
<p>@Service</p>
<p>public class EmpServiceImpl implements EmpService {</p>
<p>​    @Autowired</p>
<p>​    private TransactionTemplate txTemplate;</p>
<p>​    @Autowired</p>
<p>​    private EmpDao empDao;</p>
<p>​    @Override</p>
<p>​    public void addEmp(final Emp emp) {</p>
<p>​        txTemplate.execute(new TransactionCallbackWithoutResult() {</p>
<p>​            @Override</p>
<p>​            protected void doInTransactionWithoutResult(TransactionStatus txStatus) {</p>
<p>​                empDao.save(emp);</p>
<p>​            }</p>
<p>​        });</p>
<p>​    }</p>
<p>}</p>
<p>声明式事务如下图所示，以Spring整合Hibernate 3为例，包括完整的DAO和业务逻辑代码。</p>
<p>&lt;?xml version=”1.0” encoding=”UTF-8”?&gt;</p>
<beans xmlns="http://www.springframework.org/schema/beans" ​ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation="http://www.springframework.org/schema/beans

​           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

​           http://www.springframework.org/schema/context

​           http://www.springframework.org/schema/context/spring-context-3.2.xsd

​           http://www.springframework.org/schema/aop

​           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd

​           http://www.springframework.org/schema/tx

​           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"><br><br><br><br>​    <!-- 配置由Spring IoC容器托管的对象对应的被注解的类所在的包 --><br><br>​    &lt;context:component-scan base-package=”com.jackfrued” /&gt;<br><br><br><br>​    <!-- 配置通过自动生成代理实现AOP功能 --><br><br>​    &lt;aop:aspectj-autoproxy /&gt;<br><br><br><br>​    <!-- 配置数据库连接池 (DBCP) --><br><br>​    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" ​ destroy-method="close"><br><br>​        <!-- 配置驱动程序类 --><br><br>​        <property name="driverClassName" value="com.mysql.jdbc.Driver"><br><br>​        <!-- 配置连接数据库的URL --><br><br>​        <property name="url" value="jdbc:mysql://localhost:3306/myweb"><br><br>​        <!-- 配置访问数据库的用户名 --><br><br>​        <property name="username" value="root"><br><br>​        <!-- 配置访问数据库的口令 --><br><br>​        <property name="password" value="123456"><br><br>​        <!-- 配置最大连接数 --><br><br>​        <property name="maxActive" value="150"><br><br>​        <!-- 配置最小空闲连接数 --><br><br>​        <property name="minIdle" value="5"><br><br>​        <!-- 配置最大空闲连接数 --><br><br>​        <property name="maxIdle" value="20"><br><br>​        <!-- 配置初始连接数 --><br><br>​        <property name="initialSize" value="10"><br><br>​        <!-- 配置连接被泄露时是否生成日志 --><br><br>​        <property name="logAbandoned" value="true"><br><br>​        <!-- 配置是否删除超时连接 --><br><br>​        <property name="removeAbandoned" value="true"><br><br>​        <!-- 配置删除超时连接的超时门限值(以秒为单位) --><br><br>​        <property name="removeAbandonedTimeout" value="120"><br><br>​        <!-- 配置超时等待时间(以毫秒为单位) --><br><br>​        <property name="maxWait" value="5000"><br><br>​        <!-- 配置空闲连接回收器线程运行的时间间隔(以毫秒为单位) --><br><br>​        <property name="timeBetweenEvictionRunsMillis" value="300000"><br><br>​        <!-- 配置连接空闲多长时间后(以毫秒为单位)被断开连接 --><br><br>​        <property name="minEvictableIdleTimeMillis" value="60000"><br><br>​    </property></property></property></property></property></property></property></property></property></property></property></property></property></property></bean><br><br><br><br>​    <!-- 配置Spring提供的支持注解ORM映射的Hibernate会话工厂 --><br><br>​    <bean id="sessionFactory" ​ class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><br><br>​        <!-- 通过setter注入数据源属性 --><br><br>​        <property name="dataSource" ref="dataSource"><br><br>​        <!-- 配置实体类所在的包 --><br><br>​        <property name="packagesToScan" value="com.jackfrued.entity"><br><br>​        <!-- 配置Hibernate的相关属性 --><br><br>​        <property name="hibernateProperties"><br><br>​            <!-- 在项目调试完成后要删除show_sql和format_sql属性否则对性能有显著影响 --><br><br>​            <value><br><br>​                hibernate.dialect=org.hibernate.dialect.MySQL5Dialect<br><br>​            </value><br><br>​        </property><br><br>​    </property></property></bean><br><br><br><br>​    <!-- 配置Spring提供的Hibernate事务管理器 --><br><br>​    <bean id="transactionManager" ​ class="org.springframework.orm.hibernate3.HibernateTransactionManager"><br><br>​        <!-- 通过setter注入Hibernate会话工厂 --><br><br>​        <property name="sessionFactory" ref="sessionFactory"><br><br>​    </property></bean><br><br><br><br>​    <!-- 配置基于注解配置声明式事务 --><br><br>​    &lt;tx:annotation-driven /&gt;<br><br><br><br></beans>

<p>package com.jackfrued.dao;</p>
<p>import java.io.Serializable;</p>
<p>import java.util.List;</p>
<p>import com.jackfrued.comm.QueryBean;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>/**</p>
<ul>
<li>数据访问对象接口(以对象为单位封装CRUD操作)</li>
<li><p>@author 骆昊</p>
<p>*</p>
</li>
<li><p>@param <e> 实体类型</e></p>
</li>
<li><p>@param <k> 实体标识字段的类型</k></p>
<p>*/</p>
</li>
</ul>
<p>public interface BaseDao &lt;E, K extends Serializable&gt; {</p>
<p>​    /**</p>
<p>​     * 新增</p>
<p>​     * @param entity 业务实体对象</p>
<p>​     * @return 增加成功返回实体对象的标识</p>
<p>​     */</p>
<p>​    public K save(E entity);</p>
<p>​    /**</p>
<p>​     * 删除</p>
<p>​     * @param entity 业务实体对象</p>
<p>​     */</p>
<p>​    public void delete(E entity);</p>
<p>​    /**</p>
<p>​     * 根据ID删除</p>
<p>​     * @param id 业务实体对象的标识</p>
<p>​     * @return 删除成功返回true否则返回false</p>
<p>​     */</p>
<p>​    public boolean deleteById(K id);</p>
<p>​    /**</p>
<p>​     * 修改</p>
<p>​     * @param entity 业务实体对象</p>
<p>​     * @return 修改成功返回true否则返回false</p>
<p>​     */</p>
<p>​    public void update(E entity);</p>
<p>​    /**</p>
<p>​     * 根据ID查找业务实体对象</p>
<p>​     * @param id 业务实体对象的标识</p>
<p>​     * @return 业务实体对象对象或null</p>
<p>​     */</p>
<p>​    public E findById(K id);</p>
<p>​    /**</p>
<p>​     * 根据ID查找业务实体对象</p>
<p>​     * @param id 业务实体对象的标识</p>
<p>​     * @param lazy 是否使用延迟加载</p>
<p>​     * @return 业务实体对象对象</p>
<p>​     */</p>
<p>​    public E findById(K id, boolean lazy);</p>
<p>​    /**</p>
<p>​     * 查找所有业务实体对象</p>
<p>​     * @return 装所有业务实体对象的列表容器</p>
<p>​     */</p>
<p>​    public List<e> findAll();</e></p>
<p>​    /**</p>
<p>​     * 分页查找业务实体对象</p>
<p>​     * @param page 页码</p>
<p>​     * @param size 页面大小</p>
<p>​     * @return 查询结果对象</p>
<p>​     */</p>
<p>​    public QueryResult<e> findByPage(int page, int size);</e></p>
<p>​    /**</p>
<p>​     * 分页查找业务实体对象</p>
<p>​     * @param queryBean 查询条件对象</p>
<p>​     * @param page 页码</p>
<p>​     * @param size 页面大小</p>
<p>​     * @return 查询结果对象</p>
<p>​     */</p>
<p>​    public QueryResult<e> findByPage(QueryBean queryBean, int page, int size);</e></p>
<p>}</p>
<p>package com.jackfrued.dao;</p>
<p>import java.io.Serializable;</p>
<p>import java.util.List;</p>
<p>import com.jackfrued.comm.QueryBean;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>/**</p>
<ul>
<li>BaseDao的缺省适配器</li>
<li><p>@author 骆昊</p>
<p>*</p>
</li>
<li><p>@param <e> 实体类型</e></p>
</li>
<li><p>@param <k> 实体标识字段的类型</k></p>
<p>*/</p>
</li>
</ul>
<p>public abstract class BaseDaoAdapter&lt;E, K extends Serializable&gt; implements</p>
<p>​        BaseDao&lt;E, K&gt; {</p>
<p>​    @Override</p>
<p>​    public K save(E entity) {</p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void delete(E entity) {</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public boolean deleteById(K id) {</p>
<p>​        E entity = findById(id);</p>
<p>​        if(entity != null) {</p>
<p>​            delete(entity);</p>
<p>​            return true;</p>
<p>​        }</p>
<p>​        return false;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void update(E entity) {</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public E findById(K id) {</p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public E findById(K id, boolean lazy) {</p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public List<e> findAll() {</e></p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public QueryResult<e> findByPage(int page, int size) {</e></p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public QueryResult<e> findByPage(QueryBean queryBean, int page, int size) {</e></p>
<p>​        return null;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.dao;</p>
<p>import java.io.Serializable;</p>
<p>import java.lang.reflect.ParameterizedType;</p>
<p>import java.util.ArrayList;</p>
<p>import java.util.Collections;</p>
<p>import java.util.List;</p>
<p>import org.hibernate.Query;</p>
<p>import org.hibernate.Session;</p>
<p>import org.hibernate.SessionFactory;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;</p>
<p>import com.jackfrued.comm.HQLQueryBean;</p>
<p>import com.jackfrued.comm.QueryBean;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>/**</p>
<ul>
<li>基于Hibernate的BaseDao实现类</li>
<li><p>@author 骆昊</p>
<p>*</p>
</li>
<li><p>@param <e> 实体类型</e></p>
</li>
<li><p>@param <k> 主键类型</k></p>
<p>*/</p>
</li>
</ul>
<p>@SuppressWarnings(value = {“unchecked”})</p>
<p>public abstract class BaseDaoHibernateImpl&lt;E, K extends Serializable&gt; extends BaseDaoAdapter&lt;E, K&gt; {</p>
<p>​    @Autowired</p>
<p>​    protected SessionFactory sessionFactory;</p>
<p>​    private Class&lt;?&gt; entityClass;       // 业务实体的类对象</p>
<p>​    private String entityName;          // 业务实体的名字</p>
<p>​    public BaseDaoHibernateImpl() {</p>
<p>​        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();</p>
<p>​        entityClass = (Class&lt;?&gt;) pt.getActualTypeArguments()[0];</p>
<p>​        entityName = entityClass.getSimpleName();</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public K save(E entity) {</p>
<p>​        return (K) sessionFactory.getCurrentSession().save(entity);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void delete(E entity) {</p>
<p>​        sessionFactory.getCurrentSession().delete(entity);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void update(E entity) {</p>
<p>​        sessionFactory.getCurrentSession().update(entity);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public E findById(K id) {</p>
<p>​        return findById(id, false);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public E findById(K id, boolean lazy) {</p>
<p>​        Session session = sessionFactory.getCurrentSession();</p>
<p>​        return (E) (lazy? session.load(entityClass, id) : session.get(entityClass, id));</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public List<e> findAll() {</e></p>
<p>​        return sessionFactory.getCurrentSession().createCriteria(entityClass).list();</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public QueryResult<e> findByPage(int page, int size) {</e></p>
<p>​        return new QueryResult<e>(</e></p>
<p>​            findByHQLAndPage(“from “ + entityName , page, size),</p>
<p>​            getCountByHQL(“select count(*) from “ + entityName)</p>
<p>​        );</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public QueryResult<e> findByPage(QueryBean queryBean, int page, int size) {</e></p>
<p>​        if(queryBean instanceof HQLQueryBean) {</p>
<p>​            HQLQueryBean hqlQueryBean = (HQLQueryBean) queryBean;</p>
<p>​            return new QueryResult<e>(</e></p>
<p>​                findByHQLAndPage(hqlQueryBean.getQueryString(), page, size, hqlQueryBean.getParameters()),</p>
<p>​                getCountByHQL(hqlQueryBean.getCountString(), hqlQueryBean.getParameters())</p>
<p>​            );</p>
<p>​        }</p>
<p>​        return null;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 根据HQL和可变参数列表进行查询</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @param params 可变参数列表</p>
<p>​     * @return 持有查询结果的列表容器或空列表容器</p>
<p>​     */</p>
<p>​    protected List<e> findByHQL(String hql, Object… params) {</e></p>
<p>​        return this.findByHQL(hql, getParamList(params));</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 根据HQL和参数列表进行查询</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @param params 查询参数列表</p>
<p>​     * @return 持有查询结果的列表容器或空列表容器</p>
<p>​     */</p>
<p>​    protected List<e> findByHQL(String hql, List<object> params) {</object></e></p>
<p>​        List<e> list = createQuery(hql, params).list();</e></p>
<p>​        return list != null &amp;&amp; list.size() &gt; 0 ? list : Collections.EMPTY_LIST;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 根据HQL和参数列表进行分页查询</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @page 页码</p>
<p>​     * @size 页面大小</p>
<p>​     * @param params 可变参数列表</p>
<p>​     * @return 持有查询结果的列表容器或空列表容器</p>
<p>​     */</p>
<p>​    protected List<e> findByHQLAndPage(String hql, int page, int size, Object… params) {</e></p>
<p>​        return this.findByHQLAndPage(hql, page, size, getParamList(params));</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 根据HQL和参数列表进行分页查询</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @page 页码</p>
<p>​     * @size 页面大小</p>
<p>​     * @param params 查询参数列表</p>
<p>​     * @return 持有查询结果的列表容器或空列表容器</p>
<p>​     */</p>
<p>​    protected List<e> findByHQLAndPage(String hql, int page, int size, List<object> params) {</object></e></p>
<p>​        List<e> list = createQuery(hql, params)</e></p>
<p>​                .setFirstResult((page - 1) * size)</p>
<p>​                .setMaxResults(size)</p>
<p>​                .list();</p>
<p>​        return list != null &amp;&amp; list.size() &gt; 0 ? list : Collections.EMPTY_LIST;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 查询满足条件的记录数</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @param params 可变参数列表</p>
<p>​     * @return 满足查询条件的总记录数</p>
<p>​     */</p>
<p>​    protected long getCountByHQL(String hql, Object… params) {</p>
<p>​        return this.getCountByHQL(hql, getParamList(params));</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 查询满足条件的记录数</p>
<p>​     * @param hql 基于HQL的查询语句</p>
<p>​     * @param params 参数列表容器</p>
<p>​     * @return 满足查询条件的总记录数</p>
<p>​     */</p>
<p>​    protected long getCountByHQL(String hql, List<object> params) {</object></p>
<p>​        return (Long) createQuery(hql, params).uniqueResult();</p>
<p>​    }</p>
<p>​    // 创建Hibernate查询对象(Query)</p>
<p>​    private Query createQuery(String hql, List<object> params) {</object></p>
<p>​        Query query = sessionFactory.getCurrentSession().createQuery(hql);</p>
<p>​        for(int i = 0; i &lt; params.size(); i++) {</p>
<p>​            query.setParameter(i, params.get(i));</p>
<p>​        }</p>
<p>​        return query;</p>
<p>​    }</p>
<p>​    // 将可变参数列表组装成列表容器</p>
<p>​    private List<object> getParamList(Object… params) {</object></p>
<p>​        List<object> paramList = new ArrayList&lt;&gt;();</object></p>
<p>​        if(params != null) {</p>
<p>​            for(int i = 0; i &lt; params.length; i++) {</p>
<p>​                paramList.add(params[i]);</p>
<p>​            }</p>
<p>​        }</p>
<p>​        return paramList.size() == 0? Collections.EMPTY_LIST : paramList;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.comm;</p>
<p>import java.util.List;</p>
<p>/**</p>
<ul>
<li>查询条件的接口</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public interface QueryBean {</p>
<p>​    /**</p>
<p>​     * 添加排序字段</p>
<p>​     * @param fieldName 用于排序的字段</p>
<p>​     * @param asc 升序还是降序</p>
<p>​     * @return 查询条件对象自身(方便级联编程)</p>
<p>​     */</p>
<p>​    public QueryBean addOrder(String fieldName, boolean asc);</p>
<p>​    /**</p>
<p>​     * 添加排序字段</p>
<p>​     * @param available 是否添加此排序字段</p>
<p>​     * @param fieldName 用于排序的字段</p>
<p>​     * @param asc 升序还是降序</p>
<p>​     * @return 查询条件对象自身(方便级联编程)</p>
<p>​     */</p>
<p>​    public QueryBean addOrder(boolean available, String fieldName, boolean asc);</p>
<p>​    /**</p>
<p>​     * 添加查询条件</p>
<p>​     * @param condition 条件</p>
<p>​     * @param params 替换掉条件中参数占位符的参数</p>
<p>​     * @return 查询条件对象自身(方便级联编程)</p>
<p>​     */</p>
<p>​    public QueryBean addCondition(String condition, Object… params);</p>
<p>​    /**</p>
<p>​     * 添加查询条件</p>
<p>​     * @param available 是否需要添加此条件</p>
<p>​     * @param condition 条件</p>
<p>​     * @param params 替换掉条件中参数占位符的参数</p>
<p>​     * @return 查询条件对象自身(方便级联编程)</p>
<p>​     */</p>
<p>​    public QueryBean addCondition(boolean available, String condition, Object… params);</p>
<p>​    /**</p>
<p>​     * 获得查询语句</p>
<p>​     * @return 查询语句</p>
<p>​     */</p>
<p>​    public String getQueryString();</p>
<p>​    /**</p>
<p>​     * 获取查询记录数的查询语句</p>
<p>​     * @return 查询记录数的查询语句</p>
<p>​     */</p>
<p>​    public String getCountString();</p>
<p>​    /**</p>
<p>​     * 获得查询参数</p>
<p>​     * @return 查询参数的列表容器</p>
<p>​     */</p>
<p>​    public List<object> getParameters();</object></p>
<p>}</p>
<p>package com.jackfrued.comm;</p>
<p>import java.util.List;</p>
<p>/**</p>
<ul>
<li>查询结果</li>
<li><p>@author 骆昊</p>
<p>*</p>
</li>
<li><p>@param <t> 泛型参数</t></p>
<p>*/</p>
</li>
</ul>
<p>public class QueryResult<t> {</t></p>
<p>​    private List<t> result;     // 持有查询结果的列表容器</t></p>
<p>​    private long totalRecords;  // 查询到的总记录数</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     */</p>
<p>​    public QueryResult() {</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     * @param result 持有查询结果的列表容器</p>
<p>​     * @param totalRecords 查询到的总记录数</p>
<p>​     */</p>
<p>​    public QueryResult(List<t> result, long totalRecords) {</t></p>
<p>​        this.result = result;</p>
<p>​        this.totalRecords = totalRecords;</p>
<p>​    }</p>
<p>​    public List<t> getResult() {</t></p>
<p>​        return result;</p>
<p>​    }</p>
<p>​    public void setResult(List<t> result) {</t></p>
<p>​        this.result = result;</p>
<p>​    }</p>
<p>​    public long getTotalRecords() {</p>
<p>​        return totalRecords;</p>
<p>​    }</p>
<p>​    public void setTotalRecords(long totalRecords) {</p>
<p>​        this.totalRecords = totalRecords;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.dao;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>import com.jackfrued.entity.Dept;</p>
<p>/**</p>
<ul>
<li>部门数据访问对象接口</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public interface DeptDao extends BaseDao&lt;Dept, Integer&gt; {</p>
<p>​    /**</p>
<p>​     * 分页查询顶级部门</p>
<p>​     * @param page 页码</p>
<p>​     * @param size 页码大小</p>
<p>​     * @return 查询结果对象</p>
<p>​     */</p>
<p>​    public QueryResult<dept> findTopDeptByPage(int page, int size);</dept></p>
<p>}</p>
<p>package com.jackfrued.dao.impl;</p>
<p>import java.util.List;</p>
<p>import org.springframework.stereotype.Repository;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>import com.jackfrued.dao.BaseDaoHibernateImpl;</p>
<p>import com.jackfrued.dao.DeptDao;</p>
<p>import com.jackfrued.entity.Dept;</p>
<p>@Repository</p>
<p>public class DeptDaoImpl extends BaseDaoHibernateImpl&lt;Dept, Integer&gt; implements DeptDao {</p>
<p>​    private static final String HQL_FIND_TOP_DEPT = “ from Dept as d where d.superiorDept is null “;</p>
<p>​    @Override</p>
<p>​    public QueryResult<dept> findTopDeptByPage(int page, int size) {</dept></p>
<p>​        List<dept> list = findByHQLAndPage(HQL_FIND_TOP_DEPT, page, size);</dept></p>
<p>​        long totalRecords = getCountByHQL(“ select count(*) “ + HQL_FIND_TOP_DEPT);</p>
<p>​        return new QueryResult&lt;&gt;(list, totalRecords);</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.comm;</p>
<p>import java.util.List;</p>
<p>/**</p>
<ul>
<li>分页器</li>
<li><p>@author 骆昊</p>
<p>*</p>
</li>
<li><p>@param <t> 分页数据对象的类型</t></p>
<p>*/</p>
</li>
</ul>
<p>public class PageBean<t> {</t></p>
<p>​    private static final int DEFAUL_INIT_PAGE = 1;</p>
<p>​    private static final int DEFAULT_PAGE_SIZE = 10;</p>
<p>​    private static final int DEFAULT_PAGE_COUNT = 5;</p>
<p>​    private List<t> data;           // 分页数据</t></p>
<p>​    private PageRange pageRange;    // 页码范围</p>
<p>​    private int totalPage;          // 总页数</p>
<p>​    private int size;               // 页面大小</p>
<p>​    private int currentPage;        // 当前页码</p>
<p>​    private int pageCount;          // 页码数量</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     * @param currentPage 当前页码</p>
<p>​     * @param size 页码大小</p>
<p>​     * @param pageCount 页码数量</p>
<p>​     */</p>
<p>​    public PageBean(int currentPage, int size, int pageCount) {</p>
<p>​        this.currentPage = currentPage &gt; 0 ? currentPage : 1;</p>
<p>​        this.size = size &gt; 0 ? size : DEFAULT_PAGE_SIZE;</p>
<p>​        this.pageCount = pageCount &gt; 0 ? size : DEFAULT_PAGE_COUNT;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     * @param currentPage 当前页码</p>
<p>​     * @param size 页码大小</p>
<p>​     */</p>
<p>​    public PageBean(int currentPage, int size) {</p>
<p>​        this(currentPage, size, DEFAULT_PAGE_COUNT);</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     * @param currentPage 当前页码</p>
<p>​     */</p>
<p>​    public PageBean(int currentPage) {</p>
<p>​        this(currentPage, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_COUNT);</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     */</p>
<p>​    public PageBean() {</p>
<p>​        this(DEFAUL_INIT_PAGE, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_COUNT);</p>
<p>​    }</p>
<p>​    public List<t> getData() {</t></p>
<p>​        return data;</p>
<p>​    }</p>
<p>​    public int getStartPage() {</p>
<p>​        return pageRange != null ? pageRange.getStartPage() : 1;</p>
<p>​    }</p>
<p>​    public int getEndPage() {</p>
<p>​        return pageRange != null ? pageRange.getEndPage() : 1;</p>
<p>​    }</p>
<p>​    public long getTotalPage() {</p>
<p>​        return totalPage;</p>
<p>​    }</p>
<p>​    public int getSize() {</p>
<p>​        return size;</p>
<p>​    }</p>
<p>​    public int getCurrentPage() {</p>
<p>​        return currentPage;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 将查询结果转换为分页数据</p>
<p>​     * @param queryResult 查询结果对象</p>
<p>​     */</p>
<p>​    public void transferQueryResult(QueryResult<t> queryResult) {</t></p>
<p>​        long totalRecords = queryResult.getTotalRecords();</p>
<p>​        data = queryResult.getResult();</p>
<p>​        totalPage = (int) ((totalRecords + size - 1) / size); </p>
<p>​        totalPage = totalPage &gt;= 0 ? totalPage : Integer.MAX_VALUE;</p>
<p>​        this.pageRange = new PageRange(pageCount, currentPage, totalPage);</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.comm;</p>
<p>/**</p>
<ul>
<li>页码范围</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public class PageRange {</p>
<p>​    private int startPage;  // 起始页码</p>
<p>​    private int endPage;    // 终止页码</p>
<p>​    /**</p>
<p>​     * 构造器</p>
<p>​     * @param pageCount 总共显示几个页码</p>
<p>​     * @param currentPage 当前页码</p>
<p>​     * @param totalPage 总页数</p>
<p>​     */</p>
<p>​    public PageRange(int pageCount, int currentPage, int totalPage) {</p>
<p>​        startPage = currentPage - (pageCount - 1) / 2;</p>
<p>​        endPage = currentPage + pageCount / 2;</p>
<p>​        if(startPage &lt; 1) {</p>
<p>​            startPage = 1;</p>
<p>​            endPage = totalPage &gt; pageCount ? pageCount : totalPage;</p>
<p>​        }</p>
<p>​        if (endPage &gt; totalPage) {</p>
<p>​            endPage = totalPage;</p>
<p>​            startPage = (endPage - pageCount &gt; 0) ? endPage - pageCount + 1 : 1;</p>
<p>​        }</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 获得起始页页码</p>
<p>​     * @return 起始页页码</p>
<p>​     */</p>
<p>​    public int getStartPage() {</p>
<p>​        return startPage;</p>
<p>​    }</p>
<p>​    /**</p>
<p>​     * 获得终止页页码</p>
<p>​     * @return 终止页页码</p>
<p>​     */</p>
<p>​    public int getEndPage() {</p>
<p>​        return endPage;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.biz;</p>
<p>import com.jackfrued.comm.PageBean;</p>
<p>import com.jackfrued.entity.Dept;</p>
<p>/**</p>
<ul>
<li>部门业务逻辑接口</li>
<li><p>@author 骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public interface DeptService {</p>
<p>​    /**</p>
<p>​     * 创建新的部门</p>
<p>​     * @param department 部门对象</p>
<p>​     * @return 创建成功返回true否则返回false</p>
<p>​     */</p>
<p>​    public boolean createNewDepartment(Dept department);</p>
<p>​    /**</p>
<p>​     * 删除指定部门</p>
<p>​     * @param id 要删除的部门的编号</p>
<p>​     * @return 删除成功返回true否则返回false</p>
<p>​     */</p>
<p>​    public boolean deleteDepartment(Integer id);</p>
<p>​    /**</p>
<p>​     * 分页获取顶级部门</p>
<p>​     * @param page 页码</p>
<p>​     * @param size 页码大小</p>
<p>​     * @return 部门对象的分页器对象</p>
<p>​     */</p>
<p>​    public PageBean<dept> getTopDeptByPage(int page, int size);</dept></p>
<p>}</p>
<p>package com.jackfrued.biz.impl;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;</p>
<p>import org.springframework.stereotype.Service;</p>
<p>import org.springframework.transaction.annotation.Transactional;</p>
<p>import com.jackfrued.biz.DeptService;</p>
<p>import com.jackfrued.comm.PageBean;</p>
<p>import com.jackfrued.comm.QueryResult;</p>
<p>import com.jackfrued.dao.DeptDao;</p>
<p>import com.jackfrued.entity.Dept;</p>
<p>@Service</p>
<p>@Transactional  // 声明式事务的注解</p>
<p>public class DeptServiceImpl implements DeptService {</p>
<p>​    @Autowired</p>
<p>​    private DeptDao deptDao;</p>
<p>​    @Override</p>
<p>​    public boolean createNewDepartment(Dept department) {</p>
<p>​        return deptDao.save(department) != null;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public boolean deleteDepartment(Integer id) {</p>
<p>​        return deptDao.deleteById(id);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public PageBean<dept> getTopDeptByPage(int page, int size) {</dept></p>
<p>​        QueryResult<dept> queryResult = deptDao.findTopDeptByPage(page, size);</dept></p>
<p>​        PageBean<dept> pageBean = new PageBean&lt;&gt;(page, size);</dept></p>
<p>​        pageBean.transferQueryResult(queryResult);</p>
<p>​        return pageBean;</p>
<p>​    }</p>
<p>}</p>
<h3 id="如何在Web项目中配置Spring的IoC容器？"><a href="#如何在Web项目中配置Spring的IoC容器？" class="headerlink" title="如何在Web项目中配置Spring的IoC容器？"></a>如何在Web项目中配置Spring的IoC容器？</h3><p> 答：如果需要在Web项目中使用Spring的IoC容器，可以在Web项目配置文件web.xml中做出如下配置：</p>
<p><context-param></context-param></p>
<p>​    <param-name>contextConfigLocation</param-name></p>
<p>​    <param-value>classpath:applicationContext.xml</param-value></p>
<p></p>
<listener><br><br>​    <listener-class><br><br>​        org.springframework.web.context.ContextLoaderListener<br><br>​    </listener-class><br><br></listener>



<h3 id="如何在Spring-IoC容器中配置数据源？"><a href="#如何在Spring-IoC容器中配置数据源？" class="headerlink" title="如何在Spring IoC容器中配置数据源？"></a>如何在Spring IoC容器中配置数据源？</h3><p>答：<br>DBCP配置：</p>
<bean id="dataSource" ​ class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><br><br>​    <property name="driverClassName" value="${jdbc.driverClassName}"><br><br>​    <property name="url" value="${jdbc.url}"><br><br>​    <property name="username" value="${jdbc.username}"><br><br>​    <property name="password" value="${jdbc.password}"><br><br></property></property></property></property></bean>



<p>&lt;context:property-placeholder location=”jdbc.properties”/&gt;</p>
<p>C3P0配置：</p>
<bean id="dataSource" ​ class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><br><br>​    <property name="driverClass" value="${jdbc.driverClassName}"><br><br>​    <property name="jdbcUrl" value="${jdbc.url}"><br><br>​    <property name="user" value="${jdbc.username}"><br><br>​    <property name="password" value="${jdbc.password}"><br><br></property></property></property></property></bean>



<p>&lt;context:property-placeholder location=”jdbc.properties”/&gt;</p>
<p>提示： DBCP的详细配置在第153题中已经完整的展示过了。</p>
<h3 id="如何配置配置事务增强？"><a href="#如何配置配置事务增强？" class="headerlink" title="如何配置配置事务增强？"></a>如何配置配置事务增强？</h3><p>答：</p>
<p>&lt;?xml version=”1.0” encoding=”UTF-8”?&gt;</p>
<beans xmlns="http://www.springframework.org/schema/beans" ​ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation="

​     http://www.springframework.org/schema/beans

​     http://www.springframework.org/schema/beans/spring-beans.xsd

​     http://www.springframework.org/schema/tx

​     http://www.springframework.org/schema/tx/spring-tx.xsd

​     http://www.springframework.org/schema/aop

​     http://www.springframework.org/schema/aop/spring-aop.xsd"><br><br><br><br>  <!-- this is the service object that we want to make transactional --><br><br>  <bean id="fooService" class="x.y.service.DefaultFooService"><br><br><br><br>  <!-- the transactional advice --><br><br>  &lt;tx:advice id=”txAdvice” transaction-manager=”txManager”&gt;<br><br>  <!-- the transactional semantics... --><br><br>  tx:attributes<br><br>​    <!-- all methods starting with 'get' are read-only --><br><br>​    &lt;tx:method name=”get<em>“ read-only=”true”/&gt;<br><br>​    <!-- other methods use the default transaction settings (see below) --><br><br>​    &lt;tx:method name=”</em>“/&gt;<br><br>  /tx:attributes<br><br>  /tx:advice<br><br><br><br>  <!-- ensure that the above transactional advice runs for any execution

​    of an operation defined by the FooService interface --><br><br>  aop:config<br><br>  &lt;aop:pointcut id=”fooServiceOperation”<br><br>​    expression=”execution(<em> x.y.service.FooService.</em>(..))”/&gt;<br><br>  &lt;aop:advisor advice-ref=”txAdvice” pointcut-ref=”fooServiceOperation”/&gt;<br><br>  /aop:config<br><br><br><br>  <!-- don't forget the DataSource --><br><br>  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" ​ destroy-method="close"><br><br>  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"><br><br>  <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"><br><br>  <property name="username" value="scott"><br><br>  <property name="password" value="tiger"><br><br>  </property></property></property></property></bean><br><br><br><br>  <!-- similarly, don't forget the PlatformTransactionManager --><br><br>  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><br><br>  <property name="dataSource" ref="dataSource"><br><br>  </property></bean><br><br><br><br>  <!-- other <bean/> definitions here --><br><br><br><br></bean></beans>

<h3 id="选择使用Spring框架的原因（Spring框架为企业级开发带来的好处有哪些）？"><a href="#选择使用Spring框架的原因（Spring框架为企业级开发带来的好处有哪些）？" class="headerlink" title="选择使用Spring框架的原因（Spring框架为企业级开发带来的好处有哪些）？"></a>选择使用Spring框架的原因（Spring框架为企业级开发带来的好处有哪些）？</h3><p>答：可以从以下几个方面作答： </p>
<ul>
<li>非侵入式：支持基于POJO的编程模式，不强制性的要求实现Spring框架中的接口或继承Spring框架中的类。 </li>
<li>IoC容器：IoC容器帮助应用程序管理对象以及对象之间的依赖关系，对象之间的依赖关系如果发生了改变只需要修改配置文件而不是修改代码，因为代码的修改可能意味着项目的重新构建和完整的回归测试。有了IoC容器，程序员再也不需要自己编写工厂、单例，这一点特别符合Spring的精神”不要重复的发明轮子”。 </li>
<li>AOP（面向切面编程）：将所有的横切关注功能封装到切面（aspect）中，通过配置的方式将横切关注功能动态添加到目标代码上，进一步实现了业务逻辑和系统服务之间的分离。另一方面，有了AOP程序员可以省去很多自己写代理类的工作。 </li>
<li>MVC：Spring的MVC框架是非常优秀的，从各个方面都可以甩Struts 2几条街，为Web表示层提供了更好的解决方案。 </li>
<li>事务管理：Spring以宽广的胸怀接纳多种持久层技术，并且为其提供了声明式的事务管理，在不需要任何一行代码的情况下就能够完成事务管理。 </li>
<li>其他：选择Spring框架的原因还远不止于此，Spring为Java企业级开发提供了一站式选择，你可以在需要的时候使用它的部分和全部，更重要的是，你甚至可以在感觉不到Spring存在的情况下，在你的项目中使用Spring提供的各种优秀的功能。</li>
</ul>
<h3 id="Spring-IoC容器配置Bean的方式？"><a href="#Spring-IoC容器配置Bean的方式？" class="headerlink" title="Spring IoC容器配置Bean的方式？"></a>Spring IoC容器配置Bean的方式？</h3><p>答： </p>
<ul>
<li>基于XML文件进行配置。 </li>
<li>基于注解进行配置。 </li>
<li>基于Java程序进行配置（Spring 3+）</li>
</ul>
<p>package com.jackfrued.bean;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;</p>
<p>import org.springframework.stereotype.Component;</p>
<p>@Component</p>
<p>public class Person {</p>
<p>​    private String name;</p>
<p>​    private int age;</p>
<p>​    @Autowired</p>
<p>​    private Car car;</p>
<p>​    public Person(String name, int age) {</p>
<p>​        this.name = name;</p>
<p>​        this.age = age;</p>
<p>​    }</p>
<p>​    public void setCar(Car car) {</p>
<p>​        this.car = car;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public String toString() {</p>
<p>​        return “Person [name=” + name + “, age=” + age + “, car=” + car + “]”;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.bean;</p>
<p>import org.springframework.stereotype.Component;</p>
<p>@Component</p>
<p>public class Car {</p>
<p>​    private String brand;</p>
<p>​    private int maxSpeed;</p>
<p>​    public Car(String brand, int maxSpeed) {</p>
<p>​        this.brand = brand;</p>
<p>​        this.maxSpeed = maxSpeed;</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public String toString() {</p>
<p>​        return “Car [brand=” + brand + “, maxSpeed=” + maxSpeed + “]”;</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.config;</p>
<p>import org.springframework.context.annotation.Bean;</p>
<p>import org.springframework.context.annotation.Configuration;</p>
<p>import com.jackfrued.bean.Car;</p>
<p>import com.jackfrued.bean.Person;</p>
<p>@Configuration</p>
<p>public class AppConfig {</p>
<p>​    @Bean</p>
<p>​    public Car car() {</p>
<p>​        return new Car(“Benz”, 320);</p>
<p>​    }</p>
<p>​    @Bean</p>
<p>​    public Person person() {</p>
<p>​        return new Person(“骆昊”, 34);</p>
<p>​    }</p>
<p>}</p>
<p>package com.jackfrued.test;</p>
<p>import org.springframework.context.ConfigurableApplicationContext;</p>
<p>import org.springframework.context.annotation.AnnotationConfigApplicationContext;</p>
<p>import com.jackfrued.bean.Person;</p>
<p>import com.jackfrued.config.AppConfig;</p>
<p>class Test {</p>
<p>​    public static void main(String[] args) {</p>
<p>​        // TWR (Java 7+)</p>
<p>​        try(ConfigurableApplicationContext factory = new AnnotationConfigApplicationContext(AppConfig.class)) {</p>
<p>​            Person person = factory.getBean(Person.class);</p>
<p>​            System.out.println(person);</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<h3 id="阐述Spring框架中Bean的生命周期？"><a href="#阐述Spring框架中Bean的生命周期？" class="headerlink" title="阐述Spring框架中Bean的生命周期？"></a>阐述Spring框架中Bean的生命周期？</h3><p>答：<br>① Spring IoC容器找到关于Bean的定义并实例化该Bean。<br>② Spring IoC容器对Bean进行依赖注入。<br>③ 如果Bean实现了BeanNameAware接口，则将该Bean的id传给setBeanName方法。<br>④ 如果Bean实现了BeanFactoryAware接口，则将BeanFactory对象传给setBeanFactory方法。<br>⑤ 如果Bean实现了BeanPostProcessor接口，则调用其postProcessBeforeInitialization方法。<br>⑥ 如果Bean实现了InitializingBean接口，则调用其afterPropertySet方法。<br>⑦ 如果有和Bean关联的BeanPostProcessors对象，则这些对象的postProcessAfterInitialization方法被调用。<br>⑧ 当销毁Bean实例时，如果Bean实现了DisposableBean接口，则调用其destroy方法。</p>
<h3 id="依赖注入时如何注入集合属性？"><a href="#依赖注入时如何注入集合属性？" class="headerlink" title="依赖注入时如何注入集合属性？"></a>依赖注入时如何注入集合属性？</h3><p>答：可以在定义Bean属性时，通过<list> / <set> / <map> / <props>分别为其注入列表、集合、映射和键值都是字符串的映射属性。</props></map></set></list></p>
<h3 id="Spring中的自动装配有哪些限制？"><a href="#Spring中的自动装配有哪些限制？" class="headerlink" title="Spring中的自动装配有哪些限制？"></a>Spring中的自动装配有哪些限制？</h3><p> 答： </p>
<ul>
<li>如果使用了构造器注入或者setter注入，那么将覆盖自动装配的依赖关系。 </li>
<li>基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。 </li>
<li>优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。</li>
</ul>
<h3 id="在Web项目中如何获得Spring的IoC容器？"><a href="#在Web项目中如何获得Spring的IoC容器？" class="headerlink" title="在Web项目中如何获得Spring的IoC容器？"></a>在Web项目中如何获得Spring的IoC容器？</h3><p>答：</p>
<p>WebApplicationContext ctx = </p>
<p>WebApplicationContextUtils.getWebApplicationContext(servletContext);</p>
<h3 id="Spring由哪些模块组成？"><a href="#Spring由哪些模块组成？" class="headerlink" title="Spring由哪些模块组成？"></a>Spring由哪些模块组成？</h3><p>以下是Spring 框架的基本模块：</p>
<p>Core module</p>
<p>Bean module</p>
<p>Context module</p>
<p>Expression Language module</p>
<p>JDBC module</p>
<p>ORM module</p>
<p>OXM module</p>
<p>Java Messaging Service(JMS) module</p>
<p>Transaction module</p>
<p>Web module</p>
<p>Web-Servlet module</p>
<p>Web-Struts module</p>
<p>Web-Portlet module</p>
<h3 id="【Spring】什么是Spring-IOC-容器？"><a href="#【Spring】什么是Spring-IOC-容器？" class="headerlink" title="【Spring】什么是Spring IOC 容器？"></a>【Spring】<strong>什么是Spring IOC 容器？</strong></h3><p>Spring IOC 负责创建对象，管理对象（通过依赖注入（DI），装配对象，配置对象，并且管理这些对象的整个生命周期。</p>
<h3 id="【Spring】IOC的优点是什么？"><a href="#【Spring】IOC的优点是什么？" class="headerlink" title="【Spring】IOC的优点是什么？"></a>【Spring】<strong>IOC的优点是什么？</strong></h3><p>IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试，单元测试不再需要单例和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。</p>
<h3 id="【Spring】ApplicationContext通常的实现是什么？"><a href="#【Spring】ApplicationContext通常的实现是什么？" class="headerlink" title="【Spring】ApplicationContext通常的实现是什么？"></a>【Spring】<strong>ApplicationContext通常的实现是什么？</strong></h3><p>FileSystemXmlApplicationContext ：此容器从一个XML文件中加载beans的定义，XML Bean 配置文件的全路径名必须提供给它的构造函数。</p>
<p>ClassPathXmlApplicationContext：此容器也从一个XML文件中加载beans的定义，这里，你需要正确设置classpath因为这个容器将在classpath里找bean配置。</p>
<p>WebXmlApplicationContext：此容器加载一个XML文件，此文件定义了一个WEB应用的所有bean。</p>
<h3 id="【Spring】Bean-工厂和-Application-contexts-有什么区别？"><a href="#【Spring】Bean-工厂和-Application-contexts-有什么区别？" class="headerlink" title="【Spring】Bean 工厂和 Application contexts  有什么区别？"></a>【Spring】<strong>Bean 工厂和 Application contexts  有什么区别？</strong></h3><p>Application contexts提供一种方法处理文本消息，一个通常的做法是加载文件资源（比如镜像），它们可以向注册为监听器的bean发布事件。另外，在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作，可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口，该接口的实现以可插拔的方式提供获取本地化消息的方法。</p>
<h3 id="【Spring】什么是Spring的依赖注入？"><a href="#【Spring】什么是Spring的依赖注入？" class="headerlink" title="【Spring】什么是Spring的依赖注入？"></a>【Spring】<strong>什么是Spring的依赖注入？</strong></h3><p>依赖注入，是IOC的一个方面，是个通常的概念，它有多种解释。这概念是说你不用创建对象，而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务，但是要在配置文件里描述哪些组件需要哪些服务，之后一个容器（IOC容器）负责把他们组装起来。</p>
<h3 id="【Spring】有哪些不同类型的IOC（依赖注入）方式？"><a href="#【Spring】有哪些不同类型的IOC（依赖注入）方式？" class="headerlink" title="【Spring】有哪些不同类型的IOC（依赖注入）方式？"></a>【Spring】<strong>有哪些不同类型的IOC（依赖注入）方式？</strong></h3><p>构造器依赖注入：构造器依赖注入通过容器触发一个类的构造器来实现的，该类有一系列参数，每个参数代表一个对其他类的依赖。</p>
<p>Setter方法注入：Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后，调用该bean的setter方法，即实现了基于setter的依赖注入。</p>
<h3 id="【Spring】哪种依赖注入方式你建议使用，构造器注入，还是-Setter方法注入？"><a href="#【Spring】哪种依赖注入方式你建议使用，构造器注入，还是-Setter方法注入？" class="headerlink" title="【Spring】哪种依赖注入方式你建议使用，构造器注入，还是 Setter方法注入？"></a>【Spring】<strong>哪种依赖注入方式你建议使用，构造器注入，还是 Setter方法注入？</strong></h3><p>你两种依赖方式都可以使用，构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖，setter方法实现可选依赖。</p>
<h3 id="【Spring】什么是Spring-beans？"><a href="#【Spring】什么是Spring-beans？" class="headerlink" title="【Spring】什么是Spring beans？"></a>【Spring】<strong>什么是Spring beans？</strong></h3><p>Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化，装配，和管理。这些beans通过容器中配置的元数据创建。比如，以XML文件中<bean> 的形式定义。</bean></p>
<p>Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”，如果它被赋为TRUE，bean 就是单件，否则就是一个 prototype bean。默认是TRUE，所以所有在Spring框架中的beans 缺省都是单件。</p>
<h3 id="【Spring】一个-Spring-Bean-定义-包含什么？"><a href="#【Spring】一个-Spring-Bean-定义-包含什么？" class="headerlink" title="【Spring】一个 Spring Bean 定义 包含什么？"></a>【Spring】<strong>一个 Spring Bean 定义 包含什么？</strong></h3><p>一个Spring Bean 的定义包含容器必知的所有配置元数据，包括如何创建一个bean，它的生命周期详情及它的依赖。</p>
<h3 id="【Spring】如何给Spring-容器提供配置元数据？"><a href="#【Spring】如何给Spring-容器提供配置元数据？" class="headerlink" title="【Spring】如何给Spring 容器提供配置元数据？"></a>【Spring】<strong>如何给Spring 容器提供配置元数据？</strong></h3><p>这里有三种重要的方法给Spring 容器提供配置元数据。</p>
<p>XML配置文件。</p>
<p>基于注解的配置。</p>
<p>基于java的配置。</p>
<h3 id="【Spring】解释Spring支持的几种bean的作用域"><a href="#【Spring】解释Spring支持的几种bean的作用域" class="headerlink" title="【Spring】解释Spring支持的几种bean的作用域"></a>【Spring】<strong>解释Spring支持的几种bean的作用域</strong></h3><p>Spring框架支持以下五种bean的作用域：</p>
<p>singleton : bean在每个Spring ioc 容器中只有一个实例。</p>
<p>prototype：一个bean的定义可以有多个实例。</p>
<p>request：每次http请求都会创建一个bean，该作用域仅在基于web的Spring ApplicationContext情形下有效。</p>
<p>session：在一个HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。</p>
<p>global-session：在一个全局的HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。</p>
<p>缺省的Spring bean 的作用域是Singleton。</p>
<h3 id="【Spring】Spring框架中的单例bean是线程安全的吗？"><a href="#【Spring】Spring框架中的单例bean是线程安全的吗？" class="headerlink" title="【Spring】Spring框架中的单例bean是线程安全的吗？"></a>【Spring】<strong>Spring框架中的单例bean是线程安全的吗？</strong></h3><p>不，Spring框架中的单例bean不是线程安全的。</p>
<h3 id="【Spring】解释Spring框架中bean的生命周期"><a href="#【Spring】解释Spring框架中bean的生命周期" class="headerlink" title="【Spring】解释Spring框架中bean的生命周期"></a>【Spring】<strong>解释Spring框架中bean的生命周期</strong></h3><p>Spring容器 从XML 文件中读取bean的定义，并实例化bean。</p>
<p>Spring根据bean的定义填充所有的属性。</p>
<p>如果bean实现了BeanNameAware 接口，Spring 传递bean 的ID 到 setBeanName方法。</p>
<p>如果Bean 实现了 BeanFactoryAware 接口， Spring传递beanfactory 给setBeanFactory 方法。</p>
<p>如果有任何与bean相关联的BeanPostProcessors，Spring会在postProcesserBeforeInitialization()方法内调用它们。</p>
<p>如果bean实现IntializingBean了，调用它的afterPropertySet方法，如果bean声明了初始化方法，调用此初始化方法。</p>
<p>如果有BeanPostProcessors 和bean 关联，这些bean的postProcessAfterInitialization() 方法将被调用。</p>
<p>如果bean实现了 DisposableBean，它将调用destroy()方法。</p>
<h3 id="【Spring】哪些是重要的bean生命周期方法？-你能重载它们吗？"><a href="#【Spring】哪些是重要的bean生命周期方法？-你能重载它们吗？" class="headerlink" title="【Spring】哪些是重要的bean生命周期方法？ 你能重载它们吗？"></a>【Spring】<strong>哪些是重要的bean生命周期方法？ 你能重载它们吗？</strong></h3><p>有两个重要的bean 生命周期方法，第一个是setup ， 它是在容器加载bean的时候被调用。第二个方法是 teardown  它是在容器卸载类的时候被调用。</p>
<p>The bean 标签有两个重要的属性（init-method和destroy-method）。用它们你可以自己定制初始化和注销方法。它们也有相应的注解（@PostConstruct和@PreDestroy）。</p>
<h3 id="【Spring】在-Spring中如何注入一个java集合？"><a href="#【Spring】在-Spring中如何注入一个java集合？" class="headerlink" title="【Spring】在 Spring中如何注入一个java集合？"></a>【Spring】<strong>在 Spring中如何注入一个java集合？</strong></h3><p>Spring提供以下几种集合的配置元素：</p>
<p><list>类型用于注入一列值，允许有相同的值。</list></p>
<p><set> 类型用于注入一组值，不允许有相同的值。</set></p>
<p><map> 类型用于注入一组键值对，键和值都可以为任意类型。</map></p>
<p><props>类型用于注入一组键值对，键和值都只能为String类型。</props></p>
<h3 id="【Spring】什么是bean装配？"><a href="#【Spring】什么是bean装配？" class="headerlink" title="【Spring】什么是bean装配？"></a>【Spring】<strong>什么是bean装配？</strong></h3><p>装配，或bean 装配是指在Spring 容器中把bean组装到一起，前提是容器需要知道bean的依赖关系，如何通过依赖注入来把它们装配到一起。</p>
<h3 id="【Spring】什么是bean的自动装配？"><a href="#【Spring】什么是bean的自动装配？" class="headerlink" title="【Spring】什么是bean的自动装配？"></a>【Spring】<strong>什么是bean的自动装配？</strong></h3><p>Spring 容器能够自动装配相互合作的bean，这意味着容器不需要<constructor-arg>和<property>配置，能通过Bean工厂自动处理bean之间的协作。</property></constructor-arg></p>
<h3 id="【Spring】解释不同方式的自动装配"><a href="#【Spring】解释不同方式的自动装配" class="headerlink" title="【Spring】解释不同方式的自动装配"></a>【Spring】<strong>解释不同方式的自动装配</strong></h3><p>有五种自动装配的方式，可以用来指导Spring容器用自动装配方式来进行依赖注入</p>
<p>no：默认的方式是不进行自动装配，通过显式设置ref 属性来进行装配。</p>
<p>byName：通过参数名 自动装配，Spring容器在配置文件中发现bean的autowire属性被设置成byname，之后容器试图匹配、装配和该bean的属性具有相同名字的bean。</p>
<p>byType：通过参数类型自动装配，Spring容器在配置文件中发现bean的autowire属性被设置成byType，之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件，则抛出错误。</p>
<p>constructor：这个方式类似于byType， 但是要提供给构造器参数，如果没有确定的带参数的构造器参数类型，将会抛出异常。</p>
<p>autodetect：首先尝试使用constructor来自动装配，如果无法工作，则使用byType方式。</p>
<h3 id="【Spring】自动装配有哪些局限性？"><a href="#【Spring】自动装配有哪些局限性？" class="headerlink" title="【Spring】自动装配有哪些局限性？"></a>【Spring】<strong>自动装配有哪些局限性？</strong></h3><p>自动装配的局限性是：</p>
<p>重写：你仍需用 <constructor-arg>和 <property> 配置来定义依赖，意味着总要重写自动装配。</property></constructor-arg></p>
<p>基本数据类型：你不能自动装配简单的属性，如基本数据类型，String字符串，和类。</p>
<p>模糊特性：自动装配不如显式装配精确，如果有可能，建议使用显式装配。</p>
<h3 id="【Spring】你可以在Spring中注入一个null-和一个空字符串吗？"><a href="#【Spring】你可以在Spring中注入一个null-和一个空字符串吗？" class="headerlink" title="【Spring】你可以在Spring中注入一个null 和一个空字符串吗？"></a>【Spring】<strong>你可以在Spring中注入一个null 和一个空字符串吗？</strong></h3><p>可以</p>
<h3 id="【Spring】什么是基于Java的Spring注解配置-给一些注解的例子"><a href="#【Spring】什么是基于Java的Spring注解配置-给一些注解的例子" class="headerlink" title="【Spring】什么是基于Java的Spring注解配置? 给一些注解的例子"></a>【Spring】<strong>什么是基于Java的Spring注解配置? 给一些注解的例子</strong></h3><p>基于Java的配置，允许你在少量的Java注解的帮助下，进行你的大部分Spring配置而非通过XML文件。</p>
<p>以@Configuration 注解为例，它用来标记类可以当做一个bean的定义，被Spring IOC容器使用。另一个例子是@Bean注解，它表示此方法将要返回一个对象，作为一个bean注册进Spring应用上下文。点击这里学习JAVA几大元注解。</p>
<h3 id="【Spring】什么是基于注解的容器配置？"><a href="#【Spring】什么是基于注解的容器配置？" class="headerlink" title="【Spring】什么是基于注解的容器配置？"></a>【Spring】<strong>什么是基于注解的容器配置？</strong></h3><p>相对于XML文件，注解型的配置依赖于通过字节码元数据装配组件，而非尖括号的声明。</p>
<p>开发者通过在相应的类，方法或属性上使用注解的方式，直接组件类中进行配置，而不是使用xml表述bean的装配关系。</p>
<h3 id="【Spring】怎样开启注解装配？"><a href="#【Spring】怎样开启注解装配？" class="headerlink" title="【Spring】怎样开启注解装配？"></a>【Spring】<strong>怎样开启注解装配？</strong></h3><p>注解装配在默认情况下是不开启的，为了使用注解装配，我们必须在Spring配置文件中配置 context:annotation-config/元素。</p>
<h3 id="【Spring】-Required-注解"><a href="#【Spring】-Required-注解" class="headerlink" title="【Spring】@Required  注解"></a>【Spring】<strong>@Required  注解</strong></h3><p>这个注解表明bean的属性必须在配置的时候设置，通过一个bean定义的显式的属性值或通过自动装配，若@Required注解的bean属性未被设置，容器将抛出BeanInitializationException。</p>
<h3 id="【Spring】-Autowired-注解"><a href="#【Spring】-Autowired-注解" class="headerlink" title="【Spring】@Autowired 注解"></a>【Spring】<strong>@Autowired 注解</strong></h3><p>@Autowired 注解提供了更细粒度的控制，包括在何处以及如何完成自动装配。它的用法和@Required一样，修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。</p>
<h3 id="【Spring】-Qualifier-注解"><a href="#【Spring】-Qualifier-注解" class="headerlink" title="【Spring】@Qualifier 注解"></a>【Spring】<strong>@Qualifier 注解</strong></h3><p>当有多个相同类型的bean却只有一个需要自动装配时，将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆，指定需要装配的确切的bean。</p>
<h3 id="【Spring】在Spring框架中如何更有效地使用JDBC？"><a href="#【Spring】在Spring框架中如何更有效地使用JDBC？" class="headerlink" title="【Spring】在Spring框架中如何更有效地使用JDBC？"></a>【Spring】<strong>在Spring框架中如何更有效地使用JDBC？</strong></h3><p>使用SpringJDBC 框架，资源管理和错误处理的代价都会被减轻。所以开发者只需写statements 和 queries从数据存取数据，JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用，这个模板叫JdbcTemplate （例子见这里here）</p>
<h3 id="【Spring】-JdbcTemplate"><a href="#【Spring】-JdbcTemplate" class="headerlink" title="【Spring】 JdbcTemplate"></a>【Spring】 JdbcTemplate</h3><p>JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象，执行写好的或可调用的数据库操作语句，提供自定义的数据错误处理。</p>
<h3 id="【Spring】Spring对DAO的支持"><a href="#【Spring】Spring对DAO的支持" class="headerlink" title="【Spring】Spring对DAO的支持"></a>【Spring】<strong>Spring对DAO的支持</strong></h3><p>Spring对数据访问对象（DAO）的支持旨在简化它和数据访问技术如JDBC，Hibernate or JDO 结合使用。这使我们可以方便切换持久层。编码时也不用担心会捕获每种技术特有的异常。</p>
<h3 id="【Spring】使用Spring通过什么方式访问Hibernate？"><a href="#【Spring】使用Spring通过什么方式访问Hibernate？" class="headerlink" title="【Spring】使用Spring通过什么方式访问Hibernate？"></a>【Spring】<strong>使用Spring通过什么方式访问Hibernate？</strong></h3><p>在Spring中有两种方式访问Hibernate：</p>
<p>控制反转  Hibernate Template和 Callback</p>
<p>继承 HibernateDAOSupport提供一个AOP 拦截器</p>
<h3 id="【Spring】Spring支持的ORM"><a href="#【Spring】Spring支持的ORM" class="headerlink" title="【Spring】Spring支持的ORM"></a>【Spring】<strong>Spring支持的ORM</strong></h3><p>Spring支持以下ORM：</p>
<p>Hibernate</p>
<p>iBatis</p>
<p>JPA (Java Persistence API)</p>
<p>TopLink</p>
<p>JDO (Java Data Objects)</p>
<p>OJB</p>
<h3 id="【Spring】如何通过HibernateDaoSupport将Spring和Hibernate结合起来？"><a href="#【Spring】如何通过HibernateDaoSupport将Spring和Hibernate结合起来？" class="headerlink" title="【Spring】如何通过HibernateDaoSupport将Spring和Hibernate结合起来？"></a>【Spring】<strong>如何通过HibernateDaoSupport将Spring和Hibernate结合起来？</strong></h3><p>用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步：</p>
<p>配置the Hibernate SessionFactory</p>
<p>继承HibernateDaoSupport实现一个DAO</p>
<p>在AOP支持的事务中装配</p>
<h3 id="【Spring】Spring支持的事务管理类型"><a href="#【Spring】Spring支持的事务管理类型" class="headerlink" title="【Spring】Spring支持的事务管理类型"></a>【Spring】<strong>Spring支持的事务管理类型</strong></h3><p>Spring支持两种类型的事务管理：</p>
<p>编程式事务管理：这意味你通过编程的方式管理事务，给你带来极大的灵活性，但是难维护。</p>
<p>声明式事务管理：这意味着你可以将业务代码和事务管理分离，你只需用注解和XML配置来管理事务。</p>
<h3 id="【Spring】Spring框架的事务管理有哪些优点？"><a href="#【Spring】Spring框架的事务管理有哪些优点？" class="headerlink" title="【Spring】Spring框架的事务管理有哪些优点？"></a>【Spring】<strong>Spring框架的事务管理有哪些优点？</strong></h3><p>它为不同的事务API  如 JTA，JDBC，Hibernate，JPA 和JDO，提供一个不变的编程模式。</p>
<p>它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如</p>
<p>它支持声明式事务管理。</p>
<p>它和Spring各种数据访问抽象层很好得集成。</p>
<h3 id="【Spring】你更倾向用那种事务管理类型？"><a href="#【Spring】你更倾向用那种事务管理类型？" class="headerlink" title="【Spring】你更倾向用那种事务管理类型？"></a>【Spring】<strong>你更倾向用那种事务管理类型？</strong></h3><p>大多数Spring框架的用户选择声明式事务管理，因为它对应用代码的影响最小，因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理，虽然比编程式事务管理（这种方式允许你通过代码控制事务）少了一点灵活性。</p>
<h3 id="【Spring】-解释AOP"><a href="#【Spring】-解释AOP" class="headerlink" title="【Spring】 解释AOP"></a>【Spring】 解释AOP</h3><p>面向切面的编程，或AOP， 是一种编程技术，允许程序模块化横向切割关注点，或横切典型的责任划分，如日志和事务管理。</p>
<h3 id="【Spring】Aspect-切面"><a href="#【Spring】Aspect-切面" class="headerlink" title="【Spring】Aspect 切面"></a>【Spring】<strong>Aspect 切面</strong></h3><p>AOP核心就是切面，它将多个类的通用行为封装成可重用的模块，该模块含有一组API提供横切功能。比如，一个日志模块可以被称作日志的AOP切面。根据需求的不同，一个应用程序可以有若干切面。在Spring AOP中，切面通过带有@Aspect注解的类实现。</p>
<h3 id="【Spring】在Spring-AOP-中，关注点和横切关注的区别是什么？"><a href="#【Spring】在Spring-AOP-中，关注点和横切关注的区别是什么？" class="headerlink" title="【Spring】在Spring AOP 中，关注点和横切关注的区别是什么？"></a>【Spring】<strong>在Spring AOP 中，关注点和横切关注的区别是什么？</strong></h3><p>关注点是应用中一个模块的行为，一个关注点可能会被定义成一个我们想实现的一个功能。</p>
<p>横切关注点是一个关注点，此关注点是整个应用都会使用的功能，并影响整个应用，比如日志，安全和数据传输，几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。</p>
<h3 id="【Spring】连接点"><a href="#【Spring】连接点" class="headerlink" title="【Spring】连接点"></a>【Spring】<strong>连接点</strong></h3><p>连接点代表一个应用程序的某个位置，在这个位置我们可以插入一个AOP切面，它实际上是个应用程序执行Spring AOP的位置。</p>
<h3 id="【Spring】通知"><a href="#【Spring】通知" class="headerlink" title="【Spring】通知"></a>【Spring】<strong>通知</strong></h3><p>通知是个在方法执行前或执行后要做的动作，实际上是程序执行时要通过SpringAOP框架触发的代码段。</p>
<p>Spring切面可以应用五种类型的通知：</p>
<p>before：前置通知，在一个方法执行前被调用</p>
<p>after：在方法执行之后调用的通知，无论方法执行是否成功</p>
<p>after-returning：仅当方法成功完成后执行的通知</p>
<p>after-throwing：在方法抛出异常退出时执行的通知</p>
<p>around：在方法执行之前和之后调用的通知</p>
<h3 id="【Spring】切点"><a href="#【Spring】切点" class="headerlink" title="【Spring】切点"></a>【Spring】<strong>切点</strong></h3><p>切入点是一个或一组连接点，通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。</p>
<h3 id="【Spring】什么是引入？"><a href="#【Spring】什么是引入？" class="headerlink" title="【Spring】什么是引入？"></a>【Spring】<strong>什么是引入？</strong></h3><p>引入允许我们在已存在的类中增加新的方法和属性。</p>
<h3 id="【Spring】什么是目标对象？"><a href="#【Spring】什么是目标对象？" class="headerlink" title="【Spring】什么是目标对象？"></a>【Spring】<strong>什么是目标对象？</strong></h3><p>被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知（advised）对象。</p>
<h3 id="【Spring】什么是代理？"><a href="#【Spring】什么是代理？" class="headerlink" title="【Spring】什么是代理？"></a>【Spring】<strong>什么是代理？</strong></h3><p>代理是通知目标对象后创建的对象。从客户端的角度看，代理对象和目标对象是一样的。</p>
<h3 id="【Spring】有几种不同类型的自动代理？"><a href="#【Spring】有几种不同类型的自动代理？" class="headerlink" title="【Spring】有几种不同类型的自动代理？"></a>【Spring】<strong>有几种不同类型的自动代理？</strong></h3><p>BeanNameAutoProxyCreator</p>
<p>DefaultAdvisorAutoProxyCreator</p>
<p>Metadata autoproxying</p>
<h3 id="【Spring】什么是织入。什么是织入应用的不同点？"><a href="#【Spring】什么是织入。什么是织入应用的不同点？" class="headerlink" title="【Spring】什么是织入。什么是织入应用的不同点？"></a>【Spring】<strong>什么是织入。什么是织入应用的不同点？</strong></h3><p>织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。</p>
<p>织入可以在编译时，加载时，或运行时完成。</p>
<h3 id="【Spring】解释基于XML-Schema方式的切面实现"><a href="#【Spring】解释基于XML-Schema方式的切面实现" class="headerlink" title="【Spring】解释基于XML Schema方式的切面实现"></a>【Spring】<strong>解释基于XML Schema方式的切面实现</strong></h3><p>在这种情况下，切面由常规类以及基于XML的配置实现。</p>
<h3 id="【Spring】解释基于注解的切面实现"><a href="#【Spring】解释基于注解的切面实现" class="headerlink" title="【Spring】解释基于注解的切面实现"></a>【Spring】<strong>解释基于注解的切面实现</strong></h3><p>在这种情况下(基于@AspectJ的实现)，涉及到的切面声明的风格与带有java5标注的普通java类一致。</p>
<h3 id="【Spring】什么是Spring的MVC框架？"><a href="#【Spring】什么是Spring的MVC框架？" class="headerlink" title="【Spring】什么是Spring的MVC框架？"></a>【Spring】<strong>什么是Spring的MVC框架？</strong></h3><p>Spring 配备构建Web 应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成，如Struts，Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。</p>
<h3 id="【Spring】DispatcherServlet"><a href="#【Spring】DispatcherServlet" class="headerlink" title="【Spring】DispatcherServlet"></a>【Spring】<strong>DispatcherServlet</strong></h3><p>Spring的MVC框架是围绕DispatcherServlet来设计的，它用来处理所有的HTTP请求和响应。</p>
<h3 id="【Spring】WebApplicationContext"><a href="#【Spring】WebApplicationContext" class="headerlink" title="【Spring】WebApplicationContext"></a>【Spring】<strong>WebApplicationContext</strong></h3><p>WebApplicationContext 继承了ApplicationContext  并增加了一些WEB应用必备的特有功能，它不同于一般的ApplicationContext ，因为它能处理主题，并找到被关联的servlet。</p>
<h3 id="【Spring】什么是Spring-MVC框架的控制器？"><a href="#【Spring】什么是Spring-MVC框架的控制器？" class="headerlink" title="【Spring】什么是Spring MVC框架的控制器？"></a>【Spring】<strong>什么是Spring MVC框架的控制器？</strong></h3><p>控制器提供一个访问应用程序的行为，此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层，允许用户创建多种用途的控制器。</p>
<h3 id="【Spring】-Controller-注解"><a href="#【Spring】-Controller-注解" class="headerlink" title="【Spring】@Controller 注解"></a>【Spring】<strong>@Controller 注解</strong></h3><p>该注解表明该类扮演控制器的角色，Spring不需要你继承任何其他控制器基类或引用Servlet API。</p>
<h3 id="【Spring】-RequestMapping-注解"><a href="#【Spring】-RequestMapping-注解" class="headerlink" title="【Spring】@RequestMapping 注解"></a>【Spring】<strong>@RequestMapping 注解</strong></h3><p>该注解是用来映射一个URL到一个类或一个特定的方处理法上。</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-Mybatis-框架/">
        Java 面试题系列篇-Mybatis 框架
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="Mybatis-框架"><a href="#Mybatis-框架" class="headerlink" title="Mybatis 框架"></a>Mybatis 框架</h2><h3 id="【MyBatis】MyBatis中使用-和-书写占位符有什么区别？"><a href="#【MyBatis】MyBatis中使用-和-书写占位符有什么区别？" class="headerlink" title="【MyBatis】MyBatis中使用#和$书写占位符有什么区别？"></a>【MyBatis】MyBatis中使用#和$书写占位符有什么区别？</h3><p>答：#将传入的数据都当成一个字符串，会对传入的数据自动加上引号；将传入的数据直接显示生成在SQL中。注意：使用​占位符可能会导致SQL注射攻击，能用#的地方就不要使用，写order by子句的时候应该用​而不是#。</p>
<p>{}是预编译处理，${}是字符串替换。</p>
<p>Mybatis在处理#{}时，会将sql中的#{}替换为?号，调用PreparedStatement的set方法来赋值；</p>
<p>Mybatis在处理{}时，就是把​{}替换成变量的值。</p>
<p>使用#{}可以有效的防止SQL注入，提高系统安全性。</p>
<h3 id="【MyBatis】解释一下MyBatis中命名空间（namespace）的作用。"><a href="#【MyBatis】解释一下MyBatis中命名空间（namespace）的作用。" class="headerlink" title="【MyBatis】解释一下MyBatis中命名空间（namespace）的作用。"></a>【MyBatis】解释一下MyBatis中命名空间（namespace）的作用。</h3><p>答：在大型项目中，可能存在大量的SQL语句，这时候为每个SQL语句起一个唯一的标识（ID）就变得并不容易了。为了解决这个问题，在MyBatis中，可以为每个映射文件起一个唯一的命名空间，这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的，即使在不同映射文件中的语句ID相同，也不会再产生冲突了。</p>
<h3 id="【MyBatis】动态SQL是什么意思？"><a href="#【MyBatis】动态SQL是什么意思？" class="headerlink" title="【MyBatis】动态SQL是什么意思？"></a>【MyBatis】动态SQL是什么意思？</h3><p>答：对于一些复杂的查询，我们可能会指定多个查询条件，但是这些条件可能存在也可能不存在，例如在58同城上面找房子，我们可能会指定面积、楼层和所在位置来查找房源，也可能会指定面积、价格、户型和所在位置来查找房源，此时就需要根据用户指定的条件动态生成SQL语句。如果不使用持久层框架我们可能需要自己拼装SQL语句，还好MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实现动态SQL的元素主要有： </p>
<ul>
<li>if </li>
<li>choose / when / otherwise </li>
<li>trim </li>
<li>where </li>
<li>set </li>
<li>foreach</li>
</ul>
<p>下面是映射文件的片段。</p>
<p>​    <select id="foo" parametertype="Blog" resulttype="Blog"></select></p>
<p>​        select * from t_blog where 1 = 1</p>
<p>​        <if test="title != null"></if></p>
<p>​            and title = #{title}</p>
<p>​        </p>
<p>​        <if test="content != null"></if></p>
<p>​            and content = #{content}</p>
<p>​        </p>
<p>​        <if test="owner != null"></if></p>
<p>​            and owner = #{owner}</p>
<p>​        </p>
<p>   </p>
<p>当然也可以像下面这些书写。</p>
<p>​    <select id="foo" parametertype="Blog" resulttype="Blog"></select></p>
<p>​        select * from t_blog where 1 = 1 </p>
<p>​        <choose></choose></p>
<p>​            <when test="title != null"></when></p>
<p>​                and title = #{title}</p>
<p>​            </p>
<p>​            <when test="content != null"></when></p>
<p>​                and content = #{content}</p>
<p>​            </p>
<p>​            <otherwise></otherwise></p>
<p>​                and owner = “owner1”</p>
<p>​            </p>
<p>​        </p>
<p>​    </p>
<p>再看看下面这个例子。</p>
<p>​    <select id="bar" resulttype="Blog"></select></p>
<p>​        select * from t_blog where id in</p>
<p>​        &lt;foreach collection=”array” index=”index” </p>
<p>​            item=”item” open=”(“ separator=”,” close=”)”&gt;</p>
<p>​            #{item}</p>
<p>​        </p>
<p></p>
<h3 id="Mybatis当实体类中的属性名和表中的字段名不一样-，怎么办-？"><a href="#Mybatis当实体类中的属性名和表中的字段名不一样-，怎么办-？" class="headerlink" title="Mybatis当实体类中的属性名和表中的字段名不一样 ，怎么办 ？"></a>Mybatis当实体类中的属性名和表中的字段名不一样 ，怎么办 ？</h3><p>第1种： 通过在查询的sql语句中定义字段名的别名，让字段名的别名和实体类的属性名一致 </p>
<p>​    <select id="selectorder" parametertype="int" resultetype="me.gacl.domain.order"> </select></p>
<p>​       select order_id id, order_no orderno ,order_price price form orders where order_id=#{id}; </p>
<p> </p>
<p>第2种： 通过<resultmap>来映射字段名和实体类属性名的一一对应的关系 </resultmap></p>
<p>​    <select id="getOrder" parametertype="int" resultmap="orderresultmap"></select></p>
<p>​        select * from orders where order_id=#{id}</p>
<p>​    </p>
   <resultmap type="me.gacl.domain.order" id="orderresultmap"><br><br>​        &lt;!–用id属性来映射主键字段–&gt;<br><br>​        <id property="id" column="order_id"><br><br>​        &lt;!–用result属性来映射非主键字段，property为实体类属性名，column为数据表中的属性–&gt;<br><br>​        <result property="“orderno"" column="”order_no"/"><br><br>​        <result property="price" column="order_price"><br><br>​    <br><br><br><br>### Mybatis 模糊查询like语句该怎么写?<br><br>第1种：在Java代码中添加sql通配符。<br><br>​    string wildcardname = “%smi%”;<br><br>​    list<name> names = mapper.selectlike(wildcardname);<br><br><br><br>​    <select id="selectlike"><br><br>​     select <em> from foo where bar like #{value}<br><br>​    </em></select><br><br><br><br>第2种：在sql语句中拼接通配符，会引起sql注入<br><br>​    string wildcardname = “smi”;<br><br>​    list<name> names = mapper.selectlike(wildcardname);<br><br><br><br>​    <select id="selectlike"><br><br>​     select  from foo where bar like “%”#{value}”%”<br><br></select><br><br><br><br>### Mybatis 通常一个Xml映射文件，都会写一个Dao接口与之对应，请问，这个Dao接口的工作原理是什么？Dao接口里的方法，参数不同时，方法能重载吗？<br><br>Dao接口，就是人们常说的Mapper接口，接口的全限名，就是映射文件中的namespace的值，接口的方法名，就是映射文件中MappedStatement的id值，接口方法内的参数，就是传递给sql的参数。Mapper接口是没有实现类的，当调用接口方法时，接口全限名+方法名拼接字符串作为key值，可唯一定位一个MappedStatement，举例：com.mybatis3.mappers.StudentDao.findStudentById，可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中，每一个<select>、<insert>、<update>、<delete>标签，都会被解析为一个MappedStatement对象。<br><br><br><br>Dao接口里的方法，是不能重载的，因为是全限名+方法名的保存和寻找策略。<br><br><br><br>Dao接口的工作原理是JDK动态代理，Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象，代理对象proxy会拦截接口方法，转而执行MappedStatement所代表的sql，然后将sql执行结果返回。<br><br><br><br>### Mybatis是如何进行分页的？分页插件的原理是什么？<br><br>Mybatis使用RowBounds对象进行分页，它是针对ResultSet结果集执行的内存分页，而非物理分页，可以在sql内直接书写带有物理分页的参数来完成物理分页功能，也可以使用分页插件来完成物理分页。<br><br><br><br>分页插件的基本原理是使用Mybatis提供的插件接口，实现自定义插件，在插件的拦截方法内拦截待执行的sql，然后重写sql，根据dialect方言，添加对应的物理分页语句和物理分页参数。<br><br><br><br>### Mybatis是如何将sql执行结果封装为目标对象并返回的？都有哪些映射形式？<br><br>答：第一种是使用<resultmap>标签，逐一定义列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能，将列别名书写为对象属性名，比如T_NAME AS NAME，对象属性名一般是name，小写，但是列名不区分大小写，Mybatis会忽略列名大小写，智能找到与之对应对象属性名，你甚至可以写成T_NAME AS NaMe，Mybatis一样可以正常工作。<br><br><br><br>有了列名与属性名的映射关系后，Mybatis通过反射创建对象，同时使用反射给对象的属性逐一赋值并返回，那些找不到映射关系的属性，是无法完成赋值的。<br><br><br><br>### Mybatis 如何执行批量插入?<br><br>首先,创建一个简单的insert语句:<br><br>​    <insert id="insertname"><br><br>​     insert into names (name) values (#{value})<br><br>​    </insert><br><br>然后在java代码中像下面这样执行批处理插入:<br><br>​    list<string> names = new arraylist();<br><br>​    names.add(“fred”);<br><br>​    names.add(“barney”);<br><br>​    names.add(“betty”);<br><br>​    names.add(“wilma”);<br><br><br><br>​    // 注意这里 executortype.batch<br><br>​    sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch);<br><br>​    try {<br><br>​     namemapper mapper = sqlsession.getmapper(namemapper.class);<br><br>​     for (string name : names) {<br><br>​     mapper.insertname(name);<br><br>​     }<br><br>​     sqlsession.commit();<br><br>​    } finally {<br><br>​     sqlsession.close();<br><br>}<br><br><br><br>### Mybatis 如何获取自动生成的(主)键值?<br><br>insert 方法总是返回一个int值 - 这个值代表的是插入的行数。<br><br>而自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。<br><br>示例:<br><br>​    <insert id="insertname" usegeneratedkeys="true" keyproperty="id"><br><br>​     insert into names (name) values (#{name})<br><br>​    </insert><br><br><br><br>​    name name = new name();<br><br>​    name.setname(“fred”);<br><br><br><br>​    int rows = mapper.insertname(name);<br><br>​    // 完成后,id已经被设置到对象中<br><br>​    system.out.println(“rows inserted = ” + rows);<br><br>system.out.println(“generated key value = ” + name.getid());<br><br><br><br>### Mybatis在mapper中如何传递多个参数?<br><br>第1种：<br><br>//DAO层的函数<br><br><br><br>Public UserselectUser(String name,String area);<br><br>//对应的xml,#{0}代表接收的是dao层中的第一个参数，#{1}代表dao层中第二参数，更多参数一致往后加即可。<br><br><br><br>&lt;select id=”selectUser”resultMap=”BaseResultMap”&gt;<br><br>​    select <em>  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}<br><br></em></string></resultmap></delete></update></insert></select><br><br>第2种：    使用 @param 注解:<br><br>​    import org.apache.ibatis.annotations.param;<br><br>​        public interface usermapper {<br><br>​         user selectuser(@param(“username”) string username,<br><br>​         @param(“hashedpassword”) string hashedpassword);<br><br>​        }<br><br>然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):<br><br>​    <select id="selectuser" resulttype="user"><br><br>​         select id, username, hashedpassword<br><br>​         from some_table<br><br>​         where username = #{username}<br><br>​         and hashedpassword = #{hashedpassword}<br><br></select><br><br><br><br>### Mybatis动态sql是做什么的？都有哪些动态sql？能简述一下动态sql的执行原理不？<br><br>Mybatis动态sql可以让我们在Xml映射文件内，以标签的形式编写动态sql，完成逻辑判断和动态拼接sql的功能。<br><br>Mybatis提供了9种动态sql标签：trim|where|set|foreach|if|choose|when|otherwise|bind。<br><br>其执行原理为，使用OGNL从sql参数对象中计算表达式的值，根据表达式的值动态拼接sql，以此来完成动态sql的功能。<br><br><br><br>### Mybatis的Xml映射文件中，不同的Xml映射文件，id是否可以重复？<br><br>不同的Xml映射文件，如果配置了namespace，那么id可以重复；如果没有配置namespace，那么id不能重复；毕竟namespace不是必须的，只是最佳实践而已。<br><br><br><br>原因就是namespace+id是作为Map&lt;String, MappedStatement&gt;的key使用的，如果没有namespace，就剩下id，那么，id重复会导致数据互相覆盖。有了namespace，自然id就可以重复，namespace不同，namespace+id自然也就不同。<br><br><br><br>### Mybatis 为什么说是半自动ORM映射工具？它与全自动的区别在哪里？<br><br>Hibernate属于全自动ORM映射工具，使用Hibernate查询关联对象或者关联集合对象时，可以根据对象关系模型直接获取，所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时，需要手动编写sql来完成，所以，称之为半自动ORM映射工具。<br><br><br><br>### Mybatis 一对一、一对多的关联查询 ？<br><br><mapper namespace="com.lcb.mapping.userMapper"><br><br>​    <!--association  一对一关联查询 --><br><br>​    <select id="getClass" parametertype="int" resultmap="ClassesResultMap"><br><br>​        select  from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}<br><br>​    </select><br><br>​    <resultmap type="com.lcb.user.Classes" id="ClassesResultMap"><br><br>​        <!-- 实体类的字段名和数据表的字段名映射 --><br><br>​        <id property="id" column="c_id"><br><br>​        <result property="name" column="c_name"><br><br>​        <association property="teacher" javatype="com.lcb.user.Teacher"><br><br>​            <id property="id" column="t_id"><br><br>​            <result property="name" column="t_name"><br><br>​        </result></id></association><br><br>​    </result></id></resultmap>  



<p>​    <!--collection  一对多关联查询 -->  </p>
<p>​    <select id="getClass2" parametertype="int" resultmap="ClassesResultMap2">  </select></p>
<p>​        select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}  </p>
<p>​      </p>
<p>​    <resultmap type="com.lcb.user.Classes" id="ClassesResultMap2">  </resultmap></p>
<p>​        <id property="id" column="c_id">  </id></p>
<p>​        <result property="name" column="c_name">  </result></p>
<p>​        <association property="teacher" javatype="com.lcb.user.Teacher">  </association></p>
<p>​            <id property="id" column="t_id">  </id></p>
<p>​            <result property="name" column="t_name">  </result></p>
<p>​          </p>
<p>​        <collection property="student" oftype="com.lcb.user.Student">  </collection></p>
<p>​            <id property="id" column="s_id">  </id></p>
<p>​            <result property="name" column="s_name">  </result></p>
<p>​          </p>
<p>​    </p></mapper></name></name></result></result></id></resultmap>  <p></p>
<p>  </p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-Hibernate-框架/">
        Java 面试题系列篇-Hibernate 框架
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="Hibernate-框架"><a href="#Hibernate-框架" class="headerlink" title="Hibernate 框架"></a>Hibernate 框架</h2><h3 id="什么是ORM？"><a href="#什么是ORM？" class="headerlink" title="什么是ORM？"></a>什么是ORM？</h3><p>答：对象关系映射（Object-Relational Mapping，简称ORM）是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术；简单的说，ORM是通过使用描述对象和数据库之间映射的元数据（在Java中可以用XML或者是注解），将程序中的对象自动持久化到关系数据库中或者将关系数据库表中的行转换成Java对象，其本质上就是将数据从一种形式转换到另外一种形式。</p>
<h3 id="持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？"><a href="#持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？" class="headerlink" title="持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？"></a>持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？</h3><p> 答：所谓”持久”就是将数据保存到可掉电式存储设备中以便今后使用，简单的说，就是将内存中的数据保存到关系型数据库、文件系统、消息队列等提供持久化支持的设备中。持久层就是系统中专注于实现数据持久化的相对独立的层面。</p>
<p>持久层设计的目标包括： </p>
<ul>
<li>数据存储逻辑的分离，提供抽象化的数据访问接口。 </li>
<li>数据访问底层实现的分离，可以在不修改代码的情况下切换底层实现。 </li>
<li>资源管理和调度的分离，在数据访问层实现统一的资源调度（如缓存机制）。 </li>
<li>数据抽象，提供更面向对象的数据操作。</li>
</ul>
<p>持久层框架有： </p>
<ul>
<li>Hibernate </li>
<li>MyBatis </li>
<li>TopLink </li>
<li>Guzz </li>
<li>jOOQ </li>
<li>Spring Data </li>
<li>ActiveJDBC</li>
</ul>
<h3 id="Hibernate中SessionFactory是线程安全的吗？Session是线程安全的吗（两个线程能够共享同一个Session吗）？"><a href="#Hibernate中SessionFactory是线程安全的吗？Session是线程安全的吗（两个线程能够共享同一个Session吗）？" class="headerlink" title="Hibernate中SessionFactory是线程安全的吗？Session是线程安全的吗（两个线程能够共享同一个Session吗）？"></a>Hibernate中SessionFactory是线程安全的吗？Session是线程安全的吗（两个线程能够共享同一个Session吗）？</h3><p> 答：SessionFactory对应Hibernate的一个数据存储的概念，它是线程安全的，可以被多个线程并发访问。SessionFactory一般只会在启动的时候构建。对于应用程序，最好将SessionFactory通过单例模式进行封装以便于访问。Session是一个轻量级非线程安全的对象（线程间不能共享session），它表示与数据库进行交互的一个工作单元。Session是由SessionFactory创建的，在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。Session会延迟获取数据库连接（也就是在需要的时候才会获取）。为了避免创建太多的session，可以使用ThreadLocal将session和当前线程绑定在一起，这样可以让同一个线程获得的总是同一个session。Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。</p>
<h3 id="Hibernate中Session的load和get方法的区别是什么？"><a href="#Hibernate中Session的load和get方法的区别是什么？" class="headerlink" title="Hibernate中Session的load和get方法的区别是什么？"></a>Hibernate中Session的load和get方法的区别是什么？</h3><p>答：主要有以下三项区别：<br>① 如果没有找到符合条件的记录，get方法返回null，load方法抛出异常。<br>② get方法直接返回实体类对象，load方法返回实体类对象的代理。<br>③ 在Hibernate 3之前，get方法只在一级缓存中进行数据查找，如果没有找到对应的数据则越过二级缓存，直接发出SQL语句完成数据读取；load方法则可以从二级缓存中获取数据；从Hibernate 3开始，get方法不再是对二级缓存只写不读，它也是可以访问二级缓存的。</p>
<p>说明：对于load()方法Hibernate认为该数据在数据库中一定存在可以放心的使用代理来实现延迟加载，如果没有数据就抛出异常，而通过get()方法获取的数据可以不存在。</p>
<h3 id="Session的save-、update-、merge-、lock-、saveOrUpdate-和persist-方法分别是做什么的？有什么区别？"><a href="#Session的save-、update-、merge-、lock-、saveOrUpdate-和persist-方法分别是做什么的？有什么区别？" class="headerlink" title="Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的？有什么区别？"></a>Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的？有什么区别？</h3><p> 答：Hibernate的对象有三种状态：瞬时态（transient）、持久态（persistent）和游离态（detached），如第135题中的图所示。瞬时态的实例可以通过调用save()、persist()或者saveOrUpdate()方法变成持久态；游离态的实例可以通过调用 update()、saveOrUpdate()、lock()或者replicate()变成持久态。save()和persist()将会引发SQL的INSERT语句，而update()或merge()会引发UPDATE语句。save()和update()的区别在于一个是将瞬时态对象变成持久态，一个是将游离态对象变为持久态。merge()方法可以完成save()和update()方法的功能，它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。对于persist()方法，按照官方文档的说明：① persist()方法把一个瞬时态的实例持久化，但是并不保证标识符被立刻填入到持久化实例中，标识符的填入可能被推迟到flush的时间；② persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句，当需要封装一个长会话流程的时候，persist()方法是很有必要的；③ save()方法不保证第②条，它要返回标识符，所以它会立即执行INSERT语句，不管是在事务内部还是外部。至于lock()方法和update()方法的区别，update()方法是把一个已经更改过的脱管状态的对象变成持久状态；lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。</p>
<h3 id="阐述Session加载实体对象的过程。"><a href="#阐述Session加载实体对象的过程。" class="headerlink" title="阐述Session加载实体对象的过程。"></a>阐述Session加载实体对象的过程。</h3><p>答：Session加载实体对象的步骤是：<br>① Session在调用数据库查询功能之前，首先会在一级缓存中通过实体类型和主键进行查找，如果一级缓存查找命中且数据状态合法，则直接返回；<br>② 如果一级缓存没有命中，接下来Session会在当前NonExists记录（相当于一个查询黑名单，如果出现重复的无效查询可以迅速做出判断，从而提升性能）中进行查找，如果NonExists中存在同样的查询条件，则返回null；<br>③ 如果一级缓存查询失败则查询二级缓存，如果二级缓存命中则直接返回；<br>④ 如果之前的查询都未命中，则发出SQL语句，如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录，并返回null；<br>⑤ 根据映射配置和SQL语句得到ResultSet，并创建对应的实体对象；<br>⑥ 将对象纳入Session（一级缓存）的管理；<br>⑦ 如果有对应的拦截器，则执行拦截器的onLoad方法；<br>⑧ 如果开启并设置了要使用二级缓存，则将数据对象纳入二级缓存；<br>⑨ 返回数据对象。</p>
<h3 id="Query接口的list方法和iterate方法有什么区别？"><a href="#Query接口的list方法和iterate方法有什么区别？" class="headerlink" title="Query接口的list方法和iterate方法有什么区别？"></a>Query接口的list方法和iterate方法有什么区别？</h3><p> 答：<br>① list()方法无法利用一级缓存和二级缓存（对缓存只写不读），它只能在开启查询缓存的前提下使用查询缓存；iterate()方法可以充分利用缓存，如果目标数据只读或者读取频繁，使用iterate()方法可以减少性能开销。<br>② list()方法不会引起N+1查询问题，而iterate()方法可能引起N+1查询问题</p>
<p>说明：关于N+1查询问题，可以参考CSDN上的一篇文章《什么是N+1查询》</p>
<h3 id="Hibernate如何实现分页查询？"><a href="#Hibernate如何实现分页查询？" class="headerlink" title="Hibernate如何实现分页查询？"></a>Hibernate如何实现分页查询？</h3><p>答：通过Hibernate实现分页查询，开发人员只需要提供HQL语句（调用Session的createQuery()方法）或查询条件（调用Session的createCriteria()方法）、设置查询起始行数（调用Query或Criteria接口的setFirstResult()方法）和最大查询行数（调用Query或Criteria接口的setMaxResults()方法），并调用Query或Criteria接口的list()方法，Hibernate会自动生成分页查询的SQL语句。</p>
<h3 id="锁机制有什么用？简述Hibernate的悲观锁和乐观锁机制。"><a href="#锁机制有什么用？简述Hibernate的悲观锁和乐观锁机制。" class="headerlink" title="锁机制有什么用？简述Hibernate的悲观锁和乐观锁机制。"></a>锁机制有什么用？简述Hibernate的悲观锁和乐观锁机制。</h3><p>答：有些业务逻辑在执行过程中要求对数据进行排他性的访问，于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改，这就是所谓的锁机制。<br>Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁，顾名思义悲观的认为在数据处理过程中极有可能存在修改数据的并发事务（包括本系统的其他事务或来自外部系统的事务），于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性，关于数据库的锁机制和事务隔离级别在《Java面试题大全（上）》中已经讨论过了。乐观锁，顾名思义，对并发事务持乐观态度（认为对数据的并发操作不会经常性的发生），通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的，读取数据时获得数据的版本号，更新数据时将此版本号加1，然后和数据库表对应记录的当前版本号进行比较，如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据，否则认为是过期数据无法更新。Hibernate中通过Session的get()和load()方法从数据库中加载对象时可以通过参数指定使用悲观锁；而乐观锁可以通过给实体类加整型的版本字段再通过XML或@Version注解进行配置。</p>
<p>提示：使用乐观锁会增加了一个版本字段，很明显这需要额外的空间来存储这个版本字段，浪费了空间，但是乐观锁会让系统具有更好的并发性，这是对时间的节省。因此乐观锁也是典型的空间换时间的策略。</p>
<h3 id="阐述实体对象的三种状态以及转换关系。"><a href="#阐述实体对象的三种状态以及转换关系。" class="headerlink" title="阐述实体对象的三种状态以及转换关系。"></a>阐述实体对象的三种状态以及转换关系。</h3><p> 答：最新的Hibernate文档中为Hibernate对象定义了四种状态（原来是三种状态，面试的时候基本上问的也是三种状态），分别是：瞬时态（new, or transient）、持久态（managed, or persistent）、游状态（detached）和移除态（removed，以前Hibernate文档中定义的三种状态中没有移除态），如下图所示，就以前的Hibernate文档中移除态被视为是瞬时态。</p>
<p>瞬时态：当new一个实体对象后，这个对象处于瞬时态，即这个对象只是一个保存临时数据的内存区域，如果没有变量引用这个对象，则会被JVM的垃圾回收机制回收。这个对象所保存的数据与数据库没有任何关系，除非通过Session的save()、saveOrUpdate()、persist()、merge()方法把瞬时态对象与数据库关联，并把数据插入或者更新到数据库，这个对象才转换为持久态对象。</p>
<p>持久态：持久态对象的实例在数据库中有对应的记录，并拥有一个持久化标识（ID）。对持久态对象进行delete操作后，数据库中对应的记录将被删除，那么持久态对象与数据库记录不再存在对应关系，持久态对象变成移除态（可以视为瞬时态）。持久态对象被修改变更后，不会马上同步到数据库，直到数据库事务提交。</p>
<p>游离态：当Session进行了close()、clear()、evict()或flush()后，实体对象从持久态变成游离态，对象虽然拥有持久和与数据库对应记录一致的标识值，但是因为对象已经从会话中清除掉，对象不在持久化管理之内，所以处于游离态（也叫脱管态）。游离态的对象与临时状态对象是十分相似的，只是它还含有持久化标识。</p>
<p>提示：关于这个问题，在Hibernate的官方文档中有更为详细的解读。</p>
<h3 id="如何理解Hibernate的延迟加载机制？在实际应用中，延迟加载与Session关闭的矛盾是如何处理的？"><a href="#如何理解Hibernate的延迟加载机制？在实际应用中，延迟加载与Session关闭的矛盾是如何处理的？" class="headerlink" title="如何理解Hibernate的延迟加载机制？在实际应用中，延迟加载与Session关闭的矛盾是如何处理的？"></a>如何理解Hibernate的延迟加载机制？在实际应用中，延迟加载与Session关闭的矛盾是如何处理的？</h3><p>答：延迟加载就是并不是在读取的时候就把数据加载进来，而是等到使用时再加载。Hibernate使用了虚拟代理机制实现延迟加载，我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方，得到的都是虚拟代理，简单的说返回给用户的并不是实体本身，而是实体对象的代理。代理对象在用户调用getter方法时才会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时，数据库连接就同时关闭了。</p>
<p>延迟加载与session关闭的矛盾一般可以这样处理：<br>① 关闭延迟加载特性。这种方式操作起来比较简单，因为Hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的，但这种解决方案存在明显的缺陷。首先，出现”no session or session was closed”通常说明系统中已经存在主外键关联，如果去掉延迟加载的话，每次查询的开销都会变得很大。<br>② 在session关闭之前先获取需要查询的数据，可以使用工具方法Hibernate.isInitialized()判断对象是否被加载，如果没有被加载则可以使用Hibernate.initialize()方法加载对象。<br>③ 使用拦截器或过滤器延长Session的生命周期直到视图获得数据。Spring整合Hibernate提供的OpenSessionInViewFilter和OpenSessionInViewInterceptor就是这种做法。</p>
<h3 id="举一个多对多关联的例子，并说明如何实现多对多关联映射。"><a href="#举一个多对多关联的例子，并说明如何实现多对多关联映射。" class="headerlink" title="举一个多对多关联的例子，并说明如何实现多对多关联映射。"></a>举一个多对多关联的例子，并说明如何实现多对多关联映射。</h3><p> 答：例如：商品和订单、学生和课程都是典型的多对多关系。可以在实体类上通过@ManyToMany注解配置多对多关联或者通过映射文件中的和标签配置多对多关联，但是实际项目开发中，很多时候都是将多对多关联映射转换成两个多对一关联映射来实现的。</p>
<h3 id="谈一下你对继承映射的理解。"><a href="#谈一下你对继承映射的理解。" class="headerlink" title="谈一下你对继承映射的理解。"></a>谈一下你对继承映射的理解。</h3><p> 答：继承关系的映射策略有三种：<br>① 每个继承结构一张表（table per class hierarchy），不管多少个子类都用一张表。<br>② 每个子类一张表（table per subclass），公共信息放一张表，特有信息放单独的表。<br>③ 每个具体类一张表（table per concrete class），有多少个子类就有多少张表。<br>第一种方式属于单表策略，其优点在于查询子类对象的时候无需表连接，查询速度快，适合多态查询；缺点是可能导致表很大。后两种方式属于多表策略，其优点在于数据存储紧凑，其缺点是需要进行连接查询，不适合多态查询。</p>
<h3 id="简述Hibernate常见优化策略。"><a href="#简述Hibernate常见优化策略。" class="headerlink" title="简述Hibernate常见优化策略。"></a>简述Hibernate常见优化策略。</h3><p> 答：这个问题应当挑自己使用过的优化策略回答，常用的有：<br>① 制定合理的缓存策略（二级缓存、查询缓存）。<br>② 采用合理的Session管理机制。<br>③ 尽量使用延迟加载特性。<br>④ 设定合理的批处理参数。<br>⑤ 如果可以，选用UUID作为主键生成器。<br>⑥ 如果可以，选用基于版本号的乐观锁替代悲观锁。<br>⑦ 在开发过程中, 开启hibernate.show_sql选项查看生成的SQL，从而了解底层的状况；开发完成后关闭此选项。<br>⑧ 考虑数据库本身的优化，合理的索引、恰当的数据分区策略等都会对持久层的性能带来可观的提升，但这些需要专业的DBA（数据库管理员）提供支持。</p>
<h3 id="谈一谈Hibernate的一级缓存、二级缓存和查询缓存。"><a href="#谈一谈Hibernate的一级缓存、二级缓存和查询缓存。" class="headerlink" title="谈一谈Hibernate的一级缓存、二级缓存和查询缓存。"></a>谈一谈Hibernate的一级缓存、二级缓存和查询缓存。</h3><p>答：Hibernate的Session提供了一级缓存的功能，默认总是有效的，当应用程序保存持久化实体、修改持久化实体时，Session并不会立即把这种改变提交到数据库，而是缓存在当前的Session中，除非显示调用了Session的flush()方法或通过close()方法关闭Session。通过一级缓存，可以减少程序与数据库的交互，从而提高数据库访问性能。<br>SessionFactory级别的二级缓存是全局性的，所有的Session可以共享这个二级缓存。不过二级缓存默认是关闭的，需要显示开启并指定需要使用哪种二级缓存实现类（可以使用第三方提供的实现）。一旦开启了二级缓存并设置了需要使用二级缓存的实体类，SessionFactory就会缓存访问过的该实体类的每个对象，除非缓存的数据超出了指定的缓存空间。<br>一级缓存和二级缓存都是对整个实体进行缓存，不会缓存普通属性，如果希望对普通属性进行缓存，可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们的查询结果作为键值对进行缓存，对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的，需要显示开启。</p>
<h3 id="Hibernate中DetachedCriteria类是做什么的？"><a href="#Hibernate中DetachedCriteria类是做什么的？" class="headerlink" title="Hibernate中DetachedCriteria类是做什么的？"></a>Hibernate中DetachedCriteria类是做什么的？</h3><p>答：DetachedCriteria和Criteria的用法基本上是一致的，但Criteria是由Session的createCriteria()方法创建的，也就意味着离开创建它的Session，Criteria就无法使用了。DetachedCriteria不需要Session就可以创建（使用DetachedCriteria.forClass()方法创建），所以通常也称其为离线的Criteria，在需要进行查询操作的时候再和Session绑定（调用其getExecutableCriteria(Session)方法），这也就意味着一个DetachedCriteria可以在需要的时候和不同的Session进行绑定。</p>
<h3 id="OneToMany注解的mappedBy属性有什么作用？"><a href="#OneToMany注解的mappedBy属性有什么作用？" class="headerlink" title="@OneToMany注解的mappedBy属性有什么作用？"></a>@OneToMany注解的mappedBy属性有什么作用？</h3><p>答：@OneToMany用来配置一对多关联映射，但通常情况下，一对多关联映射都由多的一方来维护关联关系，例如学生和班级，应该在学生类中添加班级属性来维持学生和班级的关联关系（在数据库中是由学生表中的外键班级编号来维护学生表和班级表的多对一关系），如果要使用双向关联，在班级类中添加一个容器属性来存放学生，并使用@OneToMany注解进行映射，此时mappedBy属性就非常重要。如果使用XML进行配置，可以用<set>标签的inverse=”true”设置来达到同样的效果。</set></p>
<h3 id="Hibernate-的缓存原理是什么？延迟加载原理是什么？"><a href="#Hibernate-的缓存原理是什么？延迟加载原理是什么？" class="headerlink" title="Hibernate 的缓存原理是什么？延迟加载原理是什么？"></a>Hibernate 的缓存原理是什么？延迟加载原理是什么？</h3>

    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-概念解释/">
        Java 面试题系列篇-概念解释
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="概念解释"><a href="#概念解释" class="headerlink" title="概念解释"></a>概念解释</h2><h3 id="解释一下网络应用的模式及其特点。"><a href="#解释一下网络应用的模式及其特点。" class="headerlink" title="解释一下网络应用的模式及其特点。"></a>解释一下网络应用的模式及其特点。</h3><p>答：典型的网络应用模式大致有三类：B/S、C/S、P2P。其中B代表浏览器（Browser）、C代表客户端（Client）、S代表服务器（Server），P2P是对等模式，不区分客户端和服务器。B/S应用模式中可以视为特殊的C/S应用模式，只是将C/S应用模式中的特殊的客户端换成了浏览器，因为几乎所有的系统上都有浏览器，那么只要打开浏览器就可以使用应用，没有安装、配置、升级客户端所带来的各种开销。P2P应用模式中，成千上万台彼此连接的计算机都处于对等的地位，整个网络一般来说不依赖专用的集中服务器。网络中的每一台计算机既能充当网络服务的请求者，又对其它计算机的请求作出响应，提供资源和服务。通常这些资源和服务包括：信息的共享和交换、计算资源（如CPU的共享）、存储共享（如缓存和磁盘空间的使用）等，这种应用模式最大的阻力安全性、版本等问题，目前有很多应用都混合使用了多种应用模型，最常见的网络视频应用，它几乎把三种模式都用上了。</p>
<p>补充：此题要跟”电子商务模式”区分开，因为有很多人被问到这个问题的时候马上想到的是B2B（如阿里巴巴）、B2C（如当当、亚马逊、京东）、C2C（如淘宝、拍拍）、C2B（如威客）、O2O（如美团、饿了么）。对于这类问题，可以去百度上面科普一下。</p>
<h3 id="什么是Web-Service（Web服务）？"><a href="#什么是Web-Service（Web服务）？" class="headerlink" title="什么是Web Service（Web服务）？"></a>什么是Web Service（Web服务）？</h3><p>答：从表面上看，Web Service就是一个应用程序，它向外界暴露出一个能够通过Web进行调用的API。这就是说，你能够用编程的方法透明的调用这个应用程序，不需要了解它的任何细节，跟你使用的编程语言也没有关系。例如可以创建一个提供天气预报的Web Service，那么无论你用哪种编程语言开发的应用都可以通过调用它的API并传入城市信息来获得该城市的天气预报。之所以称之为Web Service，是因为它基于HTTP协议传输数据，这使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件，就可相互交换数据或集成。</p>
<p>补充：这里必须要提及的一个概念是SOA（Service-Oriented Architecture，面向服务的架构），SOA是一种思想，它将应用程序的不同功能单元通过中立的契约联系起来，独立于硬件平台、操作系统和编程语言，使得各种形式的功能单元能够更好的集成。显然，Web Service是SOA的一种较好的解决方案，它更多的是一种标准，而不是一种具体的技术。</p>
<h3 id="概念解释：SOAP、WSDL、UDDI。"><a href="#概念解释：SOAP、WSDL、UDDI。" class="headerlink" title="概念解释：SOAP、WSDL、UDDI。"></a>概念解释：SOAP、WSDL、UDDI。</h3><p>答： </p>
<ul>
<li>SOAP：简单对象访问协议（Simple Object Access Protocol），是Web Service中交换数据的一种协议规范。 </li>
<li>WSDL：Web服务描述语言（Web Service Description Language），它描述了Web服务的公共接口。这是一个基于XML的关于如何与Web服务通讯和使用的服务描述；也就是描述与目录中列出的Web服务进行交互时需要绑定的协议和信息格式。通常采用抽象语言描述该服务支持的操作和信息，使用的时候再将实际的网络协议和信息格式绑定给该服务。 </li>
<li>UDDI：统一描述、发现和集成（Universal Description, Discovery and Integration），它是一个基于XML的跨平台的描述规范，可以使世界范围内的企业在互联网上发布自己所提供的服务。简单的说，UDDI是访问各种WSDL的一个门面（可以参考设计模式中的门面模式）。</li>
</ul>
<p>提示：关于Web Service的相关概念和知识可以在W3CSchool上找到相关的资料。</p>
<h3 id="Java规范中和Web-Service相关的规范有哪些？"><a href="#Java规范中和Web-Service相关的规范有哪些？" class="headerlink" title="Java规范中和Web Service相关的规范有哪些？"></a>Java规范中和Web Service相关的规范有哪些？</h3><p>答：Java规范中和Web Service相关的有三个： </p>
<ul>
<li>JAX-WS(JSR 224)：这个规范是早期的基于SOAP的Web Service规范JAX-RPC的替代版本，它并不提供向下兼容性，因为RPC样式的WSDL以及相关的API已经在Java EE5中被移除了。WS-MetaData是JAX-WS的依赖规范，提供了基于注解配置Web Service和SOAP消息的相关API。 </li>
<li>JAXM(JSR 67)：定义了发送和接收消息所需的API,相当于Web Service的服务器端。 </li>
<li>JAX-RS(JSR 311 &amp; JSR 339 &amp; JSR 370)：是Java针对REST（Representation State Transfer）架构风格制定的一套Web Service规范。REST是一种软件架构模式，是一种风格，它不像SOAP那样本身承载着一种消息协议， (两种风格的Web Service均采用了HTTP做传输协议，因为HTTP协议能穿越防火墙，Java的远程方法调用（RMI）等是重量级协议，通常不能穿越防火墙），因此可以将REST视为基于HTTP协议的软件架构。REST中最重要的两个概念是资源定位和资源操作，而HTTP协议恰好完整的提供了这两个点。HTTP协议中的URI可以完成资源定位，而GET、POST、OPTION、DELETE方法可以完成资源操作。因此REST完全依赖HTTP协议就可以完成Web Service，而不像SOAP协议那样只利用了HTTP的传输特性，定位和操作都是由SOAP协议自身完成的，也正是由于SOAP消息的存在使得基于SOAP的Web Service显得笨重而逐渐被淘汰。</li>
</ul>
<h3 id="介绍一下你了解的Java领域的Web-Service框架。"><a href="#介绍一下你了解的Java领域的Web-Service框架。" class="headerlink" title="介绍一下你了解的Java领域的Web Service框架。"></a>介绍一下你了解的Java领域的Web Service框架。</h3><p>答：Java领域的Web Service框架很多，包括Axis2（Axis的升级版本）、Jersey（RESTful的Web Service框架）、CXF（XFire的延续版本）、Hessian、Turmeric、JBoss SOA等，其中绝大多数都是开源框架。</p>
<p>提示：面试被问到这类问题的时候一定选择自己用过的最熟悉的作答，如果之前没有了解过就应该在面试前花一些时间了解其中的两个，并比较其优缺点，这样才能在面试时给出一个漂亮的答案。</p>
<h3 id="JDK和JRE的区别是什么？"><a href="#JDK和JRE的区别是什么？" class="headerlink" title="JDK和JRE的区别是什么？"></a>JDK和JRE的区别是什么？</h3><p>Java运行时环境(JRE)。它包括Java虚拟机、Java核心类库和支持文件。它不包含开发工具（JDK）–编译器、调试器和其他工具。</p>
<p>Java开发工具包(JDK)是完整的Java软件开发包，包含了JRE，编译器和其他的工具(比如：JavaDoc，Java调试器)，可以让开发者开发、编译、执行Java应用程序。</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-JSP和servlet/">
        Java 面试题系列篇-JSP和servlet
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="JSP-和-Servlet"><a href="#JSP-和-Servlet" class="headerlink" title="JSP 和 Servlet"></a>JSP 和 Servlet</h2><h3 id="什么是JSP页面？"><a href="#什么是JSP页面？" class="headerlink" title="什么是JSP页面？"></a>什么是JSP页面？</h3><p>JSP页面是一种包含了静态数据和JSP元素两种类型的文本的文本文档。静态数据可以用任何基于文本的格式来表示，比如：HTML或者XML。JSP是一种混合了静态内容和动态产生的内容的技术。这里看下JSP的例子。</p>
<h3 id="JSP请求是如何被处理的？"><a href="#JSP请求是如何被处理的？" class="headerlink" title="JSP请求是如何被处理的？"></a>JSP请求是如何被处理的？</h3><p>浏览器首先要请求一个以.jsp扩展名结尾的页面，发起JSP请求，然后，Web服务器读取这个请求，使用JSP编译器把JSP页面转化成一个Servlet类。需要注意的是，只有当第一次请求页面或者是JSP文件发生改变的时候JSP文件才会被编译，然后服务器调用servlet类，处理浏览器的请求。一旦请求执行结束，servlet会把响应发送给客户端。这里看下如何在JSP中获取请求参数。</p>
<h3 id="JSP有哪些内置对象？作用分别是什么？"><a href="#JSP有哪些内置对象？作用分别是什么？" class="headerlink" title="JSP有哪些内置对象？作用分别是什么？"></a>JSP有哪些内置对象？作用分别是什么？</h3><p>答：JSP有9个内置对象： </p>
<ul>
<li>request：封装客户端的请求，其中包含来自GET或POST请求的参数； </li>
<li>response：封装服务器对客户端的响应； </li>
<li>pageContext：通过该对象可以获取其他对象； </li>
<li>session：封装用户会话的对象； </li>
<li>application：封装服务器运行环境的对象； </li>
<li>out：输出服务器响应的输出流对象； </li>
<li>config：Web应用的配置对象； </li>
<li>page：JSP页面本身（相当于Java程序中的this）； </li>
<li>exception：封装页面抛出异常的对象。</li>
</ul>
<p>补充：如果用Servlet来生成网页中的动态内容无疑是非常繁琐的工作，另一方面，所有的文本和HTML标签都是硬编码，即使做出微小的修改，都需要进行重新编译。JSP解决了Servlet的这些问题，它是Servlet很好的补充，可以专门用作为用户呈现视图（View），而Servlet作为控制器（Controller）专门负责处理用户请求并转发或重定向到某个页面。基于Java的Web开发很多都同时使用了Servlet和JSP。JSP页面其实是一个Servlet，能够运行Servlet的服务器（Servlet容器）通常也是JSP容器，可以提供JSP页面的运行环境，Tomcat就是一个Servlet/JSP容器。第一次请求一个JSP页面时，Servlet/JSP容器首先将JSP页面转换成一个JSP页面的实现类，这是一个实现了JspPage接口或其子接口HttpJspPage的Java类。JspPage接口是Servlet的子接口，因此每个JSP页面都是一个Servlet。转换成功后，容器会编译Servlet类，之后容器加载和实例化Java字节码，并执行它通常对Servlet所做的生命周期操作。对同一个JSP页面的后续请求，容器会查看这个JSP页面是否被修改过，如果修改过就会重新转换并重新编译并执行。如果没有则执行内存中已经存在的Servlet实例。我们可以看一段JSP代码对应的Java程序就知道一切了，而且9个内置对象的神秘面纱也会被揭开。</p>
<p>JSP页面：</p>
<p>&lt;%@ page pageEncoding=”UTF-8”%&gt;</p>
<p>&lt;%</p>
<p>String path = request.getContextPath();</p>
<p>String basePath = request.getScheme() + “://“ + request.getServerName() + “:” + request.getServerPort() + path + “/“;</p>
<p>%&gt;</p>
<p>&lt;!DOCTYPE html&gt;</p>
<html><br><br>  <head><meta name="generator" content="Hexo 3.9.0"><br><br>​    <base href="<%=basePath%>"><br><br>​    <title>首页</title><br><br>    <style type="text/css"><br><br>​        * { font-family: “Arial”; }<br><br>​    </style><br><br>  </head><br><br><br><br>  <body><br><br>​    <h1>Hello, World!</h1><br><br>​    <hr><br><br>​    <h2>Current time is: &lt;%= new java.util.Date().toString() %&gt;</h2><br><br>  </body><br><br></html>

<p>对应的Java代码：</p>
<p>/*</p>
<ul>
<li>Generated by the Jasper component of Apache Tomcat</li>
<li>Version: Apache Tomcat/7.0.52</li>
<li>Generated at: 2014-10-13 13:28:38 UTC</li>
<li>Note: The last modified time of this file was set to</li>
<li>the last modified time of the source file after</li>
<li><p>generation to assist with modification tracking.</p>
<p>*/</p>
</li>
</ul>
<p>package org.apache.jsp;</p>
<p>import javax.servlet.*;</p>
<p>import javax.servlet.http.*;</p>
<p>import javax.servlet.jsp.*;</p>
<p>public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase</p>
<p>​        implements org.apache.jasper.runtime.JspSourceDependent {</p>
<p>​    private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory</p>
<p>​            .getDefaultFactory();</p>
<p>​    private static java.util.Map&lt;java.lang.String, java.lang.Long&gt; _jspx_dependants;</p>
<p>​    private javax.el.ExpressionFactory _el_expressionfactory;</p>
<p>​    private org.apache.tomcat.InstanceManager _jsp_instancemanager;</p>
<p>​    public java.util.Map&lt;java.lang.String, java.lang.Long&gt; getDependants() {</p>
<p>​        return _jspx_dependants;</p>
<p>​    }</p>
<p>​    public void _jspInit() {</p>
<p>​        _el_expressionfactory = _jspxFactory.getJspApplicationContext(</p>
<p>​                getServletConfig().getServletContext()).getExpressionFactory();</p>
<p>​        _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory</p>
<p>​                .getInstanceManager(getServletConfig());</p>
<p>​    }</p>
<p>​    public void _jspDestroy() {</p>
<p>​    }</p>
<p>​    public void _jspService(</p>
<p>​            final javax.servlet.http.HttpServletRequest request,</p>
<p>​            final javax.servlet.http.HttpServletResponse response)</p>
<p>​            throws java.io.IOException, javax.servlet.ServletException {</p>
<p>​        // 内置对象就是在这里定义的</p>
<p>​        final javax.servlet.jsp.PageContext pageContext;</p>
<p>​        javax.servlet.http.HttpSession session = null;</p>
<p>​        final javax.servlet.ServletContext application;</p>
<p>​        final javax.servlet.ServletConfig config;</p>
<p>​        javax.servlet.jsp.JspWriter out = null;</p>
<p>​        final java.lang.Object page = this;</p>
<p>​        javax.servlet.jsp.JspWriter _jspx_out = null;</p>
<p>​        javax.servlet.jsp.PageContext _jspx_page_context = null;</p>
<p>​        try {</p>
<p>​            response.setContentType(“text/html;charset=UTF-8”);</p>
<p>​            pageContext = _jspxFactory.getPageContext(this, request, response,</p>
<p>​                    null, true, 8192, true);</p>
<p>​            _jspx_page_context = pageContext;</p>
<p>​            application = pageContext.getServletContext();</p>
<p>​            config = pageContext.getServletConfig();</p>
<p>​            session = pageContext.getSession();</p>
<p>​            out = pageContext.getOut();</p>
<p>​            _jspx_out = out;</p>
<p>​            out.write(‘\r’);</p>
<p>​            out.write(‘\n’);</p>
<p>​            String path = request.getContextPath();</p>
<p>​            String basePath = request.getScheme() + “://“</p>
<p>​                    + request.getServerName() + “:” + request.getServerPort()</p>
<p>​                    + path + “/“;</p>
<p>// 以下代码通过输出流将HTML标签输出到浏览器中</p>
<p>​            out.write(“\r\n”);</p>
<p>​            out.write(“\r\n”);</p>
<p>​            out.write(“&lt;!DOCTYPE html&gt;\r\n”);</p>
<p>​            out.write(“<html>\r\n”);</html></p>
<p>​            out.write(“  <head><meta name="generator" content="Hexo 3.9.0">\r\n”);</head></p>
<p>​            out.write(“    &lt;base href=””);</p>
<p>​            out.print(basePath);</p>
<p>​            out.write(“”&gt;\r\n”);</p>
<p>​            out.write(“    <title>首页</title>\r\n”);</p>
<p>​            out.write(“    <style type="text/css">\r\n”);</p>
<p>​            out.write(“    \t* { font-family: “Arial”; }\r\n”);</p>
<p>​            out.write(“    </style>\r\n”);</p>
<p>​            out.write(“  \r\n”);</p>
<p>​            out.write(“  \r\n”);</p>
<p>​            out.write(“  <body>\r\n”);</body></p>
<p>​            out.write(“    </p><h1>Hello, World!</h1>\r\n”);<p></p>
<p>​            out.write(“    <hr>\r\n”);</p>
<p>​            out.write(“    </p><h2>Current time is: “);<p></p>
<p>​            out.print(new java.util.Date().toString());</p>
<p>​            out.write(“</p></h2>\r\n”);<p></p>
<p>​            out.write(“  \r\n”);</p>
<p>​            out.write(“\r\n”);</p>
<p>​        } catch (java.lang.Throwable t) {</p>
<p>​            if (!(t instanceof javax.servlet.jsp.SkipPageException)) {</p>
<p>​                out = _jspx_out;</p>
<p>​                if (out != null &amp;&amp; out.getBufferSize() != 0)</p>
<p>​                    try {</p>
<p>​                        out.clearBuffer();</p>
<p>​                    } catch (java.io.IOException e) {</p>
<p>​                    }</p>
<p>​                if (_jspx_page_context != null)</p>
<p>​                    _jspx_page_context.handlePageException(t);</p>
<p>​                else</p>
<p>​                    throw new ServletException(t);</p>
<p>​            }</p>
<p>​        } finally {</p>
<p>​            jspxFactory.releasePageContext(jspx_page_context);</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<h3 id="JSP有什么优点？"><a href="#JSP有什么优点？" class="headerlink" title="JSP有什么优点？"></a>JSP有什么优点？</h3><p>下面列出了使用JSP的优点：</p>
<p>JSP页面是被动态编译成Servlet的，因此，开发者可以很容易的更新展现代码。</p>
<p>JSP页面可以被预编译。</p>
<p>JSP页面可以很容易的和静态模板结合，包括：HTML或者XML，也可以很容易的和产生动态内容的代码结合起来。</p>
<p>开发者可以提供让页面设计者以类XML格式来访问的自定义的JSP标签库。</p>
<p>开发者可以在组件层做逻辑上的改变，而不需要编辑单独使用了应用层逻辑的页面。</p>
<h3 id="什么是JSP指令-Directive-？JSP中有哪些不同类型的指令？"><a href="#什么是JSP指令-Directive-？JSP中有哪些不同类型的指令？" class="headerlink" title="什么是JSP指令(Directive)？JSP中有哪些不同类型的指令？"></a>什么是JSP指令(Directive)？JSP中有哪些不同类型的指令？</h3><p>Directive是当JSP页面被编译成Servlet的时候，JSP引擎要处理的指令。Directive用来设置页面级别的指令，从外部文件插入数据，指定自定义的标签库。Directive是定义在 &lt;%@ 和 %&gt;之间的。下面列出了不同类型的Directive：</p>
<p>包含指令(Include directive)：用来包含文件和合并文件内容到当前的页面。</p>
<p>页面指令(Page directive)：用来定义JSP页面中特定的属性，比如错误页面和缓冲区。</p>
<p>Taglib指令： 用来声明页面中使用的自定义的标签库。</p>
<h3 id="什么是JSP动作-JSP-action-？"><a href="#什么是JSP动作-JSP-action-？" class="headerlink" title="什么是JSP动作(JSP action)？"></a>什么是JSP动作(JSP action)？</h3><p>JSP动作以XML语法的结构来控制Servlet引擎的行为。当JSP页面被请求的时候，JSP动作会被执行。它们可以被动态的插入到文件中，重用JavaBean组件，转发用户到其他的页面，或者是给Java插件产生HTML代码。下面列出了可用的动作：</p>
<p>jsp:include-当JSP页面被请求的时候包含一个文件。</p>
<p>jsp:useBean-找出或者是初始化Javabean。</p>
<p>jsp:setProperty-设置JavaBean的属性。</p>
<p>jsp:getProperty-获取JavaBean的属性。</p>
<p>jsp:forward-把请求转发到新的页面。</p>
<p>jsp:plugin-产生特定浏览器的代码。</p>
<h3 id="什么是Scriptlets？"><a href="#什么是Scriptlets？" class="headerlink" title="什么是Scriptlets？"></a>什么是Scriptlets？</h3><p>JSP技术中，scriptlet是嵌入在JSP页面中的一段Java代码。scriptlet是位于标签内部的所有的东西，在标签与标签之间，用户可以添加任意有效的scriplet。</p>
<h3 id="get和post请求的区别？"><a href="#get和post请求的区别？" class="headerlink" title="get和post请求的区别？"></a>get和post请求的区别？</h3><p>答： </p>
<p>get请求用来从服务器上获得资源，而post是用来向服务器提交数据； </p>
<p>get将表单中数据按照name=value的形式，添加到action 所指向的URL 后面，并且两者使用”?”连接，而各个变量之间使用”&amp;”连接；post是将表单中的数据放在HTTP协议的请求头或消息体中，传递到action所指向URL； </p>
<p>get传输的数据要受到URL长度限制（1024字节）；而post可以传输大量的数据，上传文件通常要使用post方式； </p>
<p>使用get时参数会显示在地址栏上，如果这些数据不是敏感数据，那么可以使用get；对于敏感数据还是应用使用post； </p>
<p>get使用MIME类型application/x-www-form-urlencoded的URL编码（也叫百分号编码）文本的格式传递参数，保证被传送的参数由遵循规范的文本组成，例如一个空格的编码是”%20”。</p>
<h3 id="声明-Decalaration-在哪里？"><a href="#声明-Decalaration-在哪里？" class="headerlink" title="声明(Decalaration)在哪里？"></a>声明(Decalaration)在哪里？</h3><p>声明跟Java中的变量声明很相似，它用来声明随后要被表达式或者scriptlet使用的变量。添加的声明必须要用开始和结束标签包起来。</p>
<h3 id="什么是表达式-Expression-？"><a href="#什么是表达式-Expression-？" class="headerlink" title="什么是表达式(Expression)？"></a>什么是表达式(Expression)？</h3><p>【列表很长，可以分上、中、下发布】</p>
<p>JSP表达式是Web服务器把脚本语言表达式的值转化成一个String对象，插入到返回给客户端的数据流中。表达式是在&lt;%=和%&gt;这两个标签之间定义的。</p>
<h3 id="隐含对象是什么意思？有哪些隐含对象？"><a href="#隐含对象是什么意思？有哪些隐含对象？" class="headerlink" title="隐含对象是什么意思？有哪些隐含对象？"></a>隐含对象是什么意思？有哪些隐含对象？</h3><p>JSP隐含对象是页面中的一些Java对象，JSP容器让这些Java对象可以为开发者所使用。开发者不用明确的声明就可以直接使用他们。JSP隐含对象也叫做预定义变量。下面列出了JSP页面中的隐含对象：</p>
<p>application</p>
<p>page</p>
<p>request</p>
<p>response</p>
<p>session</p>
<p>exception</p>
<p>out</p>
<p>config</p>
<p>pageContext</p>
<h3 id="JSP和Servlet是什么关系？"><a href="#JSP和Servlet是什么关系？" class="headerlink" title="JSP和Servlet是什么关系？"></a>JSP和Servlet是什么关系？</h3><p>答：其实这个问题在上面已经阐述过了，Servlet是一个特殊的Java程序，它运行于服务器的JVM中，能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式，JSP会被服务器处理成一个类似于Servlet的Java程序，可以简化页面内容的生成。Servlet和JSP最主要的不同点在于，Servlet的应用逻辑是在Java文件中，并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。有人说，Servlet就是在Java中写HTML，而JSP就是在HTML中写Java代码，当然这个说法是很片面且不够准确的。JSP侧重于视图，Servlet更侧重于控制逻辑，在MVC架构模式中，JSP适合充当视图（view）而Servlet适合充当控制器（controller）。</p>
<h3 id="常用的Web服务器有哪些？"><a href="#常用的Web服务器有哪些？" class="headerlink" title="常用的Web服务器有哪些？"></a>常用的Web服务器有哪些？</h3><p>答：Unix和Linux平台下使用最广泛的免费HTTP服务器是Apache服务器，而Windows平台的服务器通常使用IIS作为Web服务器。选择Web服务器应考虑的因素有：性能、安全性、日志和统计、虚拟主机、代理服务器、缓冲服务和集成应用程序等。下面是对常见服务器的简介： </p>
<ul>
<li>IIS：Microsoft的Web服务器产品，全称是Internet Information Services。IIS是允许在公共Intranet或Internet上发布信息的Web服务器。IIS是目前最流行的Web服务器产品之一，很多著名的网站都是建立在IIS的平台上。IIS提供了一个图形界面的管理工具，称为Internet服务管理器，可用于监视配置和控制Internet服务。IIS是一种Web服务组件，其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器，分别用于网页浏览、文件传输、新闻服务和邮件发送等方面，它使得在网络（包括互联网和局域网）上发布信息成了一件很容易的事。它提供ISAPI(Intranet Server API）作为扩展Web服务器功能的编程接口；同时，它还提供一个Internet数据库连接器，可以实现对数据库的查询和更新。 </li>
<li>Kangle：Kangle Web服务器是一款跨平台、功能强大、安全稳定、易操作的高性能Web服务器和反向代理服务器软件。此外，Kangle也是一款专为做虚拟主机研发的Web服务器。实现虚拟主机独立进程、独立身份运行。用户之间安全隔离，一个用户出问题不影响其他用户。支持PHP、ASP、ASP.NET、Java、Ruby等多种动态开发语言。 </li>
<li>WebSphere：WebSphere Application Server是功能完善、开放的Web应用程序服务器，是IBM电子商务计划的核心部分，它是基于Java的应用环境，用于建立、部署和管理Internet和Intranet Web应用程序，适应各种Web应用程序服务器的需要。 </li>
<li>WebLogic：WebLogic Server是一款多功能、基于标准的Web应用服务器，为企业构建企业应用提供了坚实的基础。针对各种应用开发、关键性任务的部署，各种系统和数据库的集成、跨Internet协作等Weblogic都提供了相应的支持。由于它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发等优势，很多公司的企业级应用都选择它来作为开发和部署的环境。WebLogic Server在使应用服务器成为企业应用架构的基础方面一直处于领先地位，为构建集成化的企业级应用提供了稳固的基础。 </li>
<li>Apache：目前Apache仍然是世界上用得最多的Web服务器，其市场占有率很长时间都保持在60%以上（目前的市场份额约40%左右）。世界上很多著名的网站都是Apache的产物，它的成功之处主要在于它的源代码开放、有一支强大的开发团队、支持跨平台的应用（可以运行在几乎所有的Unix、Windows、Linux系统平台上）以及它的可移植性等方面。 </li>
<li>Tomcat：Tomcat是一个开放源代码、运行Servlet和JSP的容器。Tomcat实现了Servlet和JSP规范。此外，Tomcat还实现了Apache-Jakarta规范而且比绝大多数商业应用软件服务器要好，因此目前也有不少的Web服务器都选择了Tomcat。 </li>
<li>Nginx：读作”engine x”，是一个高性能的HTTP和反向代理服务器，也是一个IMAP/POP3/SMTP代理服务器。 Nginx是由Igor Sysoev为俄罗斯访问量第二的Rambler站点开发的，第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布，因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。在2014年下半年，Nginx的市场份额达到了14%。</li>
</ul>
<h3 id="如何实现JSP或Servlet的单线程模式？"><a href="#如何实现JSP或Servlet的单线程模式？" class="headerlink" title="如何实现JSP或Servlet的单线程模式？"></a>如何实现JSP或Servlet的单线程模式？</h3><p>答：<br>对于JSP页面，可以通过page指令进行设置。</p>
<p>&lt;%@page isThreadSafe=”false”%&gt;</p>
<p>1</p>
<p>对于Servlet，可以让自定义的Servlet实现SingleThreadModel标识接口。</p>
<p>说明：如果将JSP或Servlet设置成单线程工作模式，会导致每个请求创建一个Servlet实例，这种实践将导致严重的性能问题（服务器的内存压力很大，还会导致频繁的垃圾回收），所以通常情况下并不会这么做。</p>
<h3 id="实现会话跟踪的技术有哪些？"><a href="#实现会话跟踪的技术有哪些？" class="headerlink" title="实现会话跟踪的技术有哪些？"></a>实现会话跟踪的技术有哪些？</h3><p>答：由于HTTP协议本身是无状态的，服务器为了区分不同的用户，就需要对用户会话进行跟踪，简单的说就是为用户进行登记，为用户分配唯一的ID，下一次用户在请求中包含此ID，服务器据此判断到底是哪一个用户。<br>①URL 重写：在URL中添加用户会话的信息作为请求的参数，或者将唯一的会话ID添加到URL结尾以标识一个会话。<br>②设置表单隐藏域：将和会话跟踪相关的字段添加到隐式表单域中，这些信息不会在浏览器中显示但是提交表单时会提交给服务器。<br>这两种方式很难处理跨越多个页面的信息传递，因为如果每次都要修改URL或在页面中添加隐式表单域来存储用户会话相关信息，事情将变得非常麻烦。<br>③cookie：cookie有两种，一种是基于窗口的，浏览器窗口关闭后，cookie就没有了；另一种是将信息存储在一个临时文件中，并设置存在的时间。当用户通过浏览器和服务器建立一次会话后，会话ID就会随响应信息返回存储在基于窗口的cookie中，那就意味着只要浏览器没有关闭，会话没有超时，下一次请求时这个会话ID又会提交给服务器让服务器识别用户身份。会话中可以为用户保存信息。会话对象是在服务器内存中的，而基于窗口的cookie是在客户端内存中的。如果浏览器禁用了cookie，那么就需要通过下面两种方式进行会话跟踪。当然，在使用cookie时要注意几点：首先不要在cookie中存放敏感信息；其次cookie存储的数据量有限（4k），不能将过多的内容存储cookie中；再者浏览器通常只允许一个站点最多存放20个cookie。当然，和用户会话相关的其他信息（除了会话ID）也可以存在cookie方便进行会话跟踪。<br>④HttpSession：在所有会话跟踪技术中，HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建HttpSession，每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方法获得HttpSession，通过HttpSession的setAttribute方法可以将一个值放在HttpSession中，通过调用HttpSession对象的getAttribute方法，同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的是，HttpSession放在服务器的内存中，因此不要将过大的对象放在里面，即使目前的Servlet容器可以在内存将满时将HttpSession中的对象移到其他存储设备中，但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象，这个对象最好实现了Serializable接口，这样Servlet容器在必要的时候可以将其序列化到文件中，否则在序列化时就会出现异常。</p>
<p>补充：HTML5中可以使用Web Storage技术通过JavaScript来保存数据，例如可以使用localStorage和sessionStorage来保存用户会话的信息，也能够实现会话跟踪。</p>
<h3 id="过滤器有哪些作用和用法？"><a href="#过滤器有哪些作用和用法？" class="headerlink" title="过滤器有哪些作用和用法？"></a>过滤器有哪些作用和用法？</h3><p> 答： Java Web开发中的过滤器（filter）是从Servlet 2.3规范开始增加的功能，并在Servlet 2.4规范中得到增强。对Web应用来说，过滤器是一个驻留在服务器端的Web组件，它可以截取客户端和服务器之间的请求与响应信息，并对这些信息进行过滤。当Web容器接受到一个对资源的请求时，它将判断是否有过滤器与这个资源相关联。如果有，那么容器将把请求交给过滤器进行处理。在过滤器中，你可以改变请求的内容，或者重新设置请求的报头信息，然后再将请求发送给目标资源。当目标资源对请求作出响应时候，容器同样会将响应先转发给过滤器，在过滤器中你可以对响应的内容进行转换，然后再将响应发送到客户端。</p>
<p>常见的过滤器用途主要包括：对用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件、对XML的输出应用XSLT等。</p>
<p>和过滤器相关的接口主要有：Filter、FilterConfig和FilterChain。</p>
<p>编码过滤器的例子：</p>
<p>import java.io.IOException;</p>
<p>import javax.servlet.Filter;</p>
<p>import javax.servlet.FilterChain;</p>
<p>import javax.servlet.FilterConfig;</p>
<p>import javax.servlet.ServletException;</p>
<p>import javax.servlet.ServletRequest;</p>
<p>import javax.servlet.ServletResponse;</p>
<p>import javax.servlet.annotation.WebFilter;</p>
<p>import javax.servlet.annotation.WebInitParam;</p>
<p>@WebFilter(urlPatterns = { “*” }, </p>
<p>​        initParams = {@WebInitParam(name=”encoding”, value=”utf-8”)})</p>
<p>public class CodingFilter implements Filter {</p>
<p>​    private String defaultEncoding = “utf-8”;</p>
<p>​    @Override</p>
<p>​    public void destroy() {</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void doFilter(ServletRequest req, ServletResponse resp,</p>
<p>​            FilterChain chain) throws IOException, ServletException {</p>
<p>​        req.setCharacterEncoding(defaultEncoding);</p>
<p>​        resp.setCharacterEncoding(defaultEncoding);</p>
<p>​        chain.doFilter(req, resp);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void init(FilterConfig config) throws ServletException {</p>
<p>​        String encoding = config.getInitParameter(“encoding”);</p>
<p>​        if (encoding != null) {</p>
<p>​            defaultEncoding = encoding;</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<p>下载计数过滤器的例子：</p>
<p>import java.io.File;</p>
<p>import java.io.FileReader;</p>
<p>import java.io.FileWriter;</p>
<p>import java.io.IOException;</p>
<p>import java.util.Properties;</p>
<p>import java.util.concurrent.ExecutorService;</p>
<p>import java.util.concurrent.Executors;</p>
<p>import javax.servlet.Filter;</p>
<p>import javax.servlet.FilterChain;</p>
<p>import javax.servlet.FilterConfig;</p>
<p>import javax.servlet.ServletException;</p>
<p>import javax.servlet.ServletRequest;</p>
<p>import javax.servlet.ServletResponse;</p>
<p>import javax.servlet.annotation.WebFilter;</p>
<p>import javax.servlet.http.HttpServletRequest;</p>
<p>@WebFilter(urlPatterns = {“/*”})</p>
<p>public class DownloadCounterFilter implements Filter {</p>
<p>​    private ExecutorService executorService = Executors.newSingleThreadExecutor();</p>
<p>​    private Properties downloadLog;</p>
<p>​    private File logFile;</p>
<p>​    @Override</p>
<p>​    public void destroy() {</p>
<p>​        executorService.shutdown();</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void doFilter(ServletRequest req, ServletResponse resp,</p>
<p>​            FilterChain chain) throws IOException, ServletException {</p>
<p>​        HttpServletRequest request = (HttpServletRequest) req;</p>
<p>​        final String uri = request.getRequestURI();</p>
<p>​        executorService.execute(new Runnable() {</p>
<p>​            @Override</p>
<p>​            public void run() {</p>
<p>​                String value = downloadLog.getProperty(uri);</p>
<p>​                if(value == null) {</p>
<p>​                    downloadLog.setProperty(uri, “1”);</p>
<p>​                }</p>
<p>​                else {</p>
<p>​                    int count = Integer.parseInt(value);</p>
<p>​                    downloadLog.setProperty(uri, String.valueOf(++count));</p>
<p>​                }</p>
<p>​                try {</p>
<p>​                    downloadLog.store(new FileWriter(logFile), “”);</p>
<p>​                } </p>
<p>​                catch (IOException e) {</p>
<p>​                    e.printStackTrace();</p>
<p>​                }</p>
<p>​            }</p>
<p>​        });</p>
<p>​        chain.doFilter(req, resp);</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void init(FilterConfig config) throws ServletException {</p>
<p>​        String appPath = config.getServletContext().getRealPath(“/“);</p>
<p>​        logFile = new File(appPath, “downloadLog.txt”);</p>
<p>​        if(!logFile.exists()) {</p>
<p>​            try {</p>
<p>​                logFile.createNewFile();</p>
<p>​            } </p>
<p>​            catch(IOException e) {</p>
<p>​                e.printStackTrace();</p>
<p>​            }</p>
<p>​        }</p>
<p>​        downloadLog = new Properties();</p>
<p>​        try {</p>
<p>​            downloadLog.load(new FileReader(logFile));</p>
<p>​        } catch (IOException e) {</p>
<p>​            e.printStackTrace();</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<p>说明：这里使用了Servlet 3规范中的注解来部署过滤器，当然也可以在web.xml中使用<filter>和<filter-mapping>标签部署过滤器，如108题中所示。</filter-mapping></filter></p>
<h3 id="监听器有哪些作用和用法？"><a href="#监听器有哪些作用和用法？" class="headerlink" title="监听器有哪些作用和用法？"></a>监听器有哪些作用和用法？</h3><p>答：Java Web开发中的监听器（listener）就是application、session、request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件，如下所示：<br>①ServletContextListener：对Servlet上下文的创建和销毁进行监听。<br>②ServletContextAttributeListener：监听Servlet上下文属性的添加、删除和替换。<br>③HttpSessionListener：对Session的创建和销毁进行监听。</p>
<p>补充：session的销毁有两种情况：1). session超时（可以在web.xml中通过<session-config>/<session-timeout>标签配置超时时间）；2). 通过调用session对象的invalidate()方法使session失效。</session-timeout></session-config></p>
<p>④HttpSessionAttributeListener：对Session对象中属性的添加、删除和替换进行监听。<br>⑤ServletRequestListener：对请求对象的初始化和销毁进行监听。<br>⑥ServletRequestAttributeListener：对请求对象属性的添加、删除和替换进行监听。</p>
<p>下面是一个统计网站最多在线人数监听器的例子。</p>
<p>import javax.servlet.ServletContextEvent;</p>
<p>import javax.servlet.ServletContextListener;</p>
<p>import javax.servlet.annotation.WebListener;</p>
<p>/**</p>
<p> 上下文监听器，在服务器启动时初始化onLineCount和maxOnLineCount两个变量</p>
<p> 并将其置于服务器上下文（ServletContext）中，其初始值都是0</p>
<p>*/</p>
<p>@WebListener</p>
<p>public class InitListener implements ServletContextListener {</p>
<p>​    @Override</p>
<p>​    public void contextDestroyed(ServletContextEvent evt) {</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void contextInitialized(ServletContextEvent evt) {</p>
<p>​        evt.getServletContext().setAttribute(“onLineCount”, 0);</p>
<p>​        evt.getServletContext().setAttribute(“maxOnLineCount”, 0);</p>
<p>​    }</p>
<p>}</p>
<p>import java.text.DateFormat;</p>
<p>import java.text.SimpleDateFormat;</p>
<p>import java.util.Date;</p>
<p>import javax.servlet.ServletContext;</p>
<p>import javax.servlet.annotation.WebListener;</p>
<p>import javax.servlet.http.HttpSessionEvent;</p>
<p>import javax.servlet.http.HttpSessionListener;</p>
<p>/**</p>
<p> 会话监听器，在用户会话创建和销毁的时候根据情况</p>
<p> 修改onLineCount和maxOnLineCount的值</p>
<p>*/</p>
<p>@WebListener</p>
<p>public class MaxCountListener implements HttpSessionListener {</p>
<p>​    @Override</p>
<p>​    public void sessionCreated(HttpSessionEvent event) {</p>
<p>​        ServletContext ctx = event.getSession().getServletContext();</p>
<p>​        int count = Integer.parseInt(ctx.getAttribute(“onLineCount”).toString());</p>
<p>​        count++;</p>
<p>​        ctx.setAttribute(“onLineCount”, count);</p>
<p>​        int maxOnLineCount = Integer.parseInt(ctx.getAttribute(“maxOnLineCount”).toString());</p>
<p>​        if (count &gt; maxOnLineCount) {</p>
<p>​            ctx.setAttribute(“maxOnLineCount”, count);</p>
<p>​            DateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);</p>
<p>​            ctx.setAttribute(“date”, df.format(new Date()));</p>
<p>​        }</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public void sessionDestroyed(HttpSessionEvent event) {</p>
<p>​        ServletContext app = event.getSession().getServletContext();</p>
<p>​        int count = Integer.parseInt(app.getAttribute(“onLineCount”).toString());</p>
<p>​        count–;</p>
<p>​        app.setAttribute(“onLineCount”, count);</p>
<p>​    }</p>
<p>}</p>
<p>说明：这里使用了Servlet 3规范中的@WebListener注解配置监听器，当然你可以在web.xml文件中用<listener>标签配置监听器，如108题中所示。</listener></p>
<h3 id="web-xml文件中可以配置哪些内容？"><a href="#web-xml文件中可以配置哪些内容？" class="headerlink" title="web.xml文件中可以配置哪些内容？"></a>web.xml文件中可以配置哪些内容？</h3><p>答：web.xml用于配置Web应用的相关信息，如：监听器（listener）、过滤器（filter）、 Servlet、相关参数、会话超时时间、安全验证方式、错误页面等，下面是一些开发中常见的配置：</p>
<p>①配置Spring上下文加载监听器加载Spring配置文件并创建IoC容器：</p>
<p>  <context-param></context-param></p>
<p>​     <param-name>contextConfigLocation</param-name></p>
<p>​    <param-value>classpath:applicationContext.xml</param-value></p>
<p>  </p>
  <listener><br><br>​     <listener-class><br><br>​       org.springframework.web.context.ContextLoaderListener<br><br>​     </listener-class><br><br>  </listener>

<p>②配置Spring的OpenSessionInView过滤器来解决延迟加载和Hibernate会话关闭的矛盾：</p>
  <filter><br><br>​      <filter-name>openSessionInView</filter-name><br><br>​      <filter-class><br><br>​         org.springframework.orm.hibernate3.support.OpenSessionInViewFilter<br><br>​      </filter-class><br><br>  </filter>



<p>  <filter-mapping></filter-mapping></p>
<p>​      <filter-name>openSessionInView</filter-name></p>
<p>​      <url-pattern>/*</url-pattern></p>
<p>  </p>
<p>③配置会话超时时间为10分钟：</p>
<p>  <session-config></session-config></p>
<p>​      <session-timeout>10</session-timeout></p>
<p>  </p>
<p>④配置404和Exception的错误页面：</p>
<p>  <error-page></error-page></p>
<p>​      <error-code>404</error-code></p>
<p>​      <location>/error.jsp</location></p>
<p>  </p>
<p>  <error-page></error-page></p>
<p>​      <exception-type>java.lang.Exception</exception-type></p>
<p>​      <location>/error.jsp</location></p>
<p>  </p>
<p>⑤配置安全认证方式：</p>
<p>  <security-constraint></security-constraint></p>
<p>​      <web-resource-collection></web-resource-collection></p>
<p>​          <web-resource-name>ProtectedArea</web-resource-name></p>
<p>​          <url-pattern>/admin/*</url-pattern></p>
<p>​          <http-method>GET</http-method></p>
<p>​          <http-method>POST</http-method></p>
<p>​      </p>
<p>​      <auth-constraint></auth-constraint></p>
<p>​          <role-name>admin</role-name></p>
<p>​      </p>
<p>  </p>
<p>  <login-config></login-config></p>
<p>​      <auth-method>BASIC</auth-method></p>
<p>  </p>
<p>  <security-role></security-role></p>
<p>​      <role-name>admin</role-name></p>
<p>  </p>
<p>说明：对Servlet（小服务）、Listener（监听器）和Filter（过滤器）等Web组件的配置，Servlet 3规范提供了基于注解的配置方式，可以分别使用@WebServlet、@WebListener、@WebFilter注解进行配置。 </p>
<p>补充：如果Web提供了有价值的商业信息或者是敏感数据，那么站点的安全性就是必须考虑的问题。安全认证是实现安全性的重要手段，认证就是要解决“Are you who you say you are?”的问题。认证的方式非常多，简单说来可以分为三类：<br>A. What you know? — 口令<br>B. What you have? — 数字证书（U盾、密保卡）<br>C. Who you are? — 指纹识别、虹膜识别<br>在Tomcat中可以通过建立安全套接字层（Secure Socket Layer, SSL）以及通过基本验证或表单验证来实现对安全性的支持。</p>
<h3 id="你的项目中使用过哪些JSTL标签？"><a href="#你的项目中使用过哪些JSTL标签？" class="headerlink" title="你的项目中使用过哪些JSTL标签？"></a>你的项目中使用过哪些JSTL标签？</h3><p>答：项目中主要使用了JSTL的核心标签库，包括c:if、c:choose、&lt;c: when&gt;、&lt;c: otherwise&gt;、c:forEach等，主要用于构造循环和分支结构以控制显示逻辑。</p>
<p>说明：虽然JSTL标签库提供了core、sql、fmt、xml等标签库，但是实际开发中建议只使用核心标签库（core），而且最好只使用分支和循环标签并辅以表达式语言（EL），这样才能真正做到数据显示和业务逻辑的分离，这才是最佳实践。</p>
<h3 id="使用标签库有什么好处？如何自定义JSP标签？"><a href="#使用标签库有什么好处？如何自定义JSP标签？" class="headerlink" title="使用标签库有什么好处？如何自定义JSP标签？"></a>使用标签库有什么好处？如何自定义JSP标签？</h3><p>答：使用标签库的好处包括以下几个方面： </p>
<ul>
<li>分离JSP页面的内容和逻辑，简化了Web开发； </li>
<li>开发者可以创建自定义标签来封装业务逻辑和显示逻辑； </li>
<li>标签具有很好的可移植性、可维护性和可重用性； </li>
<li>避免了对Scriptlet（小脚本）的使用（很多公司的项目开发都不允许在JSP中书写小脚本）</li>
</ul>
<p>自定义JSP标签包括以下几个步骤： </p>
<ul>
<li>编写一个Java类实现实现Tag/BodyTag/IterationTag接口（开发中通常不直接实现这些接口而是继承TagSupport/BodyTagSupport/SimpleTagSupport类，这是对缺省适配模式的应用），重写doStartTag()、doEndTag()等方法，定义标签要完成的功能 </li>
<li>编写扩展名为tld的标签描述文件对自定义标签进行部署，tld文件通常放在WEB-INF文件夹下或其子目录中 </li>
<li>在JSP页面中使用taglib指令引用该标签库</li>
</ul>
<p>下面是一个自定义标签库的例子。</p>
<p>步骤1 - 标签类源代码TimeTag.java：</p>
<p>package com.jackfrued.tags;</p>
<p>import java.io.IOException;</p>
<p>import java.text.SimpleDateFormat;</p>
<p>import java.util.Date;</p>
<p>import javax.servlet.jsp.JspException;</p>
<p>import javax.servlet.jsp.JspWriter;</p>
<p>import javax.servlet.jsp.tagext.TagSupport;</p>
<p>public class TimeTag extends TagSupport {</p>
<p>​    private static final long serialVersionUID = 1L;</p>
<p>​    private String format = “yyyy-MM-dd hh:mm:ss”;</p>
<p>​    private String foreColor = “black”;</p>
<p>​    private String backColor = “white”;</p>
<p>​    public int doStartTag() throws JspException {</p>
<p>​         SimpleDateFormat sdf = new SimpleDateFormat(format);</p>
<p>​         JspWriter writer = pageContext.getOut();</p>
<p>​         StringBuilder sb = new StringBuilder();</p>
<p>​         sb.append(String.format(“%s”,</p>
<p>​             foreColor, backColor, sdf.format(new Date())));</p>
<p>​         try {</p>
<p>​           writer.print(sb.toString());</p>
<p>​         } catch(IOException e) {</p>
<p>​           e.printStackTrace();</p>
<p>​         }</p>
<p>​         return SKIP_BODY;</p>
<p>​      }</p>
<p>​    public void setFormat(String format) {</p>
<p>​        this.format = format;</p>
<p>​    }</p>
<p>​    public void setForeColor(String foreColor) {</p>
<p>​        this.foreColor = foreColor;</p>
<p>​    }</p>
<p>​    public void setBackColor(String backColor) {</p>
<p>​        this.backColor = backColor;</p>
<p>​    }</p>
<p>}</p>
<p>步骤2 - 编写标签库描述文件my.tld：</p>
<p>&lt;?xml version=”1.0” encoding=”UTF-8” ?&gt;</p>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" ​ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee 

​    http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"><br><br><br><br>​    <description>定义标签库</description><br><br>​    <tlib-version>1.0</tlib-version><br><br>​    <short-name>MyTag</short-name><br><br>​    <tag><br><br>​        <name>time</name><br><br>​        <tag-class>com.jackfrued.tags.TimeTag</tag-class><br><br>​        <body-content>empty</body-content><br><br>​        <attribute><br><br>​            <name>format</name><br><br>​            <required>false</required><br><br>​        </attribute><br><br>​        <attribute><br><br>​            <name>foreColor</name><br><br>​        </attribute><br><br>​        <attribute><br><br>​            <name>backColor</name><br><br>​        </attribute><br><br>​    </tag><br><br></taglib>

<p>步骤3 - 在JSP页面中使用自定义标签：</p>
<p>&lt;%@ page pageEncoding=”UTF-8”%&gt;</p>
<p>&lt;%@ taglib prefix=”my” uri=”/WEB-INF/tld/my.tld” %&gt;</p>
<p>&lt;%</p>
<p>String path = request.getContextPath();</p>
<p>String basePath = request.getScheme() + “://“ + request.getServerName() + “:” + request.getServerPort() + path + “/“;</p>
<p>%&gt;</p>
<p>&lt;!DOCTYPE html&gt;</p>
<html><br><br>  <head><meta name="generator" content="Hexo 3.9.0"><br><br>​    <base href="<%=basePath%>"><br><br>​    <title>首页</title><br><br>    <style type="text/css"><br><br>​        * { font-family: “Arial”; font-size:72px; }<br><br>​    </style><br><br>  </head><br><br><br><br>  <body><br><br>​    &lt;my:time format=”yyyy-MM-dd” backColor=”blue” foreColor=”yellow”/&gt;<br><br>  </body><br><br></html>



<p>提示：如果要将自定义的标签库发布成JAR文件，需要将标签库描述文件（tld文件）放在JAR文件的META-INF目录下，可以JDK中的jar工具完成JAR文件的生成，如果不清楚如何操作，可以请教谷老师和百老师。</p>
<h3 id="说一下表达式语言（EL）的隐式对象及其作用。"><a href="#说一下表达式语言（EL）的隐式对象及其作用。" class="headerlink" title="说一下表达式语言（EL）的隐式对象及其作用。"></a>说一下表达式语言（EL）的隐式对象及其作用。</h3><p> 答：EL的隐式对象包括：pageContext、initParam（访问上下文参数）、param（访问请求参数）、paramValues、header（访问请求头）、headerValues、cookie（访问cookie）、applicationScope（访问application作用域）、sessionScope（访问session作用域）、requestScope（访问request作用域）、pageScope（访问page作用域）。</p>
<p>用法如下所示：</p>
<p>${pageContext.request.method}</p>
<p>${pageContext”request”}</p>
<p>${pageContext.request[“method”]}</p>
<p>${pageContext[“request”].method}</p>
<p>${initParam.defaultEncoding}</p>
<p>${header[“accept-language”]}</p>
<p>${headerValues”accept-language”}</p>
<p>${cookie.jsessionid.value}</p>
<p>${sessionScope.loginUser.username}</p>
<p>补充：表达式语言的.和[]运算作用是一致的，唯一的差别在于如果访问的属性名不符合Java标识符命名规则，例如上面的accept-language就不是一个有效的Java标识符，那么这时候就只能用[]运算符而不能使用.运算符获取它的值</p>
<h3 id="表达式语言（EL）支持哪些运算符？"><a href="#表达式语言（EL）支持哪些运算符？" class="headerlink" title="表达式语言（EL）支持哪些运算符？"></a>表达式语言（EL）支持哪些运算符？</h3><p>答：除了.和[]运算符，EL还提供了： </p>
<ul>
<li>算术运算符：+、-、*、/或div、%或mod </li>
<li>关系运算符：==或eq、!=或ne、&gt;或gt、&gt;=或ge、&lt;或lt、&lt;=或le </li>
<li>逻辑运算符：&amp;&amp;或and、||或or、!或not </li>
<li>条件运算符：${statement? A : B}（跟Java的条件运算符类似） </li>
<li>empty运算符：检查一个值是否为null或者空（数组长度为0或集合中没有元素也返回true）</li>
</ul>
<h3 id="Java-Web开发的Model-1和Model-2分别指的是什么？"><a href="#Java-Web开发的Model-1和Model-2分别指的是什么？" class="headerlink" title="Java Web开发的Model 1和Model 2分别指的是什么？"></a>Java Web开发的Model 1和Model 2分别指的是什么？</h3><p>答：Model 1是以页面为中心的Java Web开发，使用JSP+JavaBean技术将页面显示逻辑和业务逻辑处理分开，JSP实现页面显示，JavaBean对象用来保存数据和实现业务逻辑。Model 2是基于MVC（模型-视图-控制器，Model-View-Controller）架构模式的开发模型，实现了模型和视图的彻底分离，利于团队开发和代码复用，如下图所示。</p>
<h3 id="Servlet-3中的异步处理指的是什么？"><a href="#Servlet-3中的异步处理指的是什么？" class="headerlink" title="Servlet 3中的异步处理指的是什么？"></a>Servlet 3中的异步处理指的是什么？</h3><p>答：在Servlet 3中引入了一项新的技术可以让Servlet异步处理请求。有人可能会质疑，既然都有多线程了，还需要异步处理请求吗？答案是肯定的，因为如果一个任务处理时间相当长，那么Servlet或Filter会一直占用着请求处理线程直到任务结束，随着并发用户的增加，容器将会遭遇线程超出的风险，这这种情况下很多的请求将会被堆积起来而后续的请求可能会遭遇拒绝服务，直到有资源可以处理请求为止。异步特性可以帮助应用节省容器中的线程，特别适合执行时间长而且用户需要得到结果的任务，如果用户不需要得到结果则直接将一个Runnable对象交给Executor并立即返回即可。（如果不清楚多线程和线程池的相关内容，请查看《Java面试题全集（上）》关于多线程和线程池的部分或阅读我的另一篇文章《关于Java并发编程的总结和思考》）</p>
<p>补充：多线程在Java诞生初期无疑是一个亮点，而Servlet单实例多线程的工作方式也曾为其赢得美名，然而技术的发展往往会颠覆我们很多的认知，就如同当年爱因斯坦的相对论颠覆了牛顿的经典力学一般。事实上，异步处理绝不是Serlvet 3首创，如果你了解Node.js的话，对Servlet 3的这个重要改进就不以为奇了。</p>
<p>下面是一个支持异步处理请求的Servlet的例子。</p>
<p>import java.io.IOException;</p>
<p>import javax.servlet.AsyncContext;</p>
<p>import javax.servlet.ServletException;</p>
<p>import javax.servlet.annotation.WebServlet;</p>
<p>import javax.servlet.http.HttpServlet;</p>
<p>import javax.servlet.http.HttpServletRequest;</p>
<p>import javax.servlet.http.HttpServletResponse;</p>
<p>@WebServlet(urlPatterns = {“/async”}, asyncSupported = true)</p>
<p>public class AsyncServlet extends HttpServlet {</p>
<p>​    private static final long serialVersionUID = 1L;</p>
<p>​    @Override</p>
<p>​    public void doGet(HttpServletRequest req, HttpServletResponse resp) </p>
<p>​            throws ServletException, IOException {</p>
<p>​        // 开启Tomcat异步Servlet支持</p>
<p>​        req.setAttribute(“org.apache.catalina.ASYNC_SUPPORTED”, true);</p>
<p>​        final AsyncContext ctx = req.startAsync();  // 启动异步处理的上下文</p>
<p>​        // ctx.setTimeout(30000);</p>
<p>​        ctx.start(new Runnable() {</p>
<p>​            @Override</p>
<p>​            public void run() {</p>
<p>​                // 在此处添加异步处理的代码</p>
<p>​                ctx.complete();</p>
<p>​            }</p>
<p>​        });</p>
<p>​    }</p>
<p>}</p>
<h3 id="讲解JSP中的四种作用域。"><a href="#讲解JSP中的四种作用域。" class="headerlink" title="讲解JSP中的四种作用域。"></a>讲解JSP中的四种作用域。</h3><p>答：JSP中的四种作用域包括page、request、session和application，具体来说： </p>
<ul>
<li>page代表与一个页面相关的对象和属性。 </li>
<li>request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面，涉及多个Web组件；需要在页面显示的临时数据可以置于此作用域。 </li>
<li>session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 </li>
<li>application代表与整个Web应用程序相关的对象和属性，它实质上是跨越整个Web应用程序，包括多个页面、请求和会话的一个全局作用域。</li>
</ul>
<h3 id="如何在基于Java的Web项目中实现文件上传和下载？"><a href="#如何在基于Java的Web项目中实现文件上传和下载？" class="headerlink" title="如何在基于Java的Web项目中实现文件上传和下载？"></a>如何在基于Java的Web项目中实现文件上传和下载？</h3><p>答：在Sevlet 3 以前，Servlet API中没有支持上传功能的API，因此要实现上传功能需要引入第三方工具从POST请求中获得上传的附件或者通过自行处理输入流来获得上传的文件，我们推荐使用Apache的commons-fileupload。<br>从Servlet 3开始，文件上传变得无比简单，相信看看下面的例子一切都清楚了。</p>
<p>上传页面index.jsp：</p>
<p>&lt;%@ page pageEncoding=”utf-8”%&gt;</p>
<p>&lt;!DOCTYPE html&gt;</p>
<html><br><br><head><meta name="generator" content="Hexo 3.9.0"><br><br><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><br><br><title>Photo Upload</title><br><br></head><br><br><body><br><br><h1>Select your photo and upload</h1><br><br><hr><br><br><div style="color:red;font-size:14px;">${hint}</div><br><br><form action="UploadServlet" method="post" enctype="multipart/form-data"><br><br>​    Photo file: <input type="file" name="photo"><br><br>​    <input type="submit" value="Upload"><br><br></form><br><br></body><br><br></html>

<p>支持上传的Servlet：</p>
<p>package com.jackfrued.servlet;</p>
<p>import java.io.IOException;</p>
<p>import javax.servlet.ServletException;</p>
<p>import javax.servlet.annotation.MultipartConfig;</p>
<p>import javax.servlet.annotation.WebServlet;</p>
<p>import javax.servlet.http.HttpServlet;</p>
<p>import javax.servlet.http.HttpServletRequest;</p>
<p>import javax.servlet.http.HttpServletResponse;</p>
<p>import javax.servlet.http.Part;</p>
<p>@WebServlet(“/UploadServlet”)</p>
<p>@MultipartConfig</p>
<p>public class UploadServlet extends HttpServlet {</p>
<p>​    private static final long serialVersionUID = 1L;</p>
<p>​    protected void doPost(HttpServletRequest request,</p>
<p>​            HttpServletResponse response) throws ServletException, IOException {</p>
<p>​        // 可以用request.getPart()方法获得名为photo的上传附件</p>
<p>​        // 也可以用request.getParts()获得所有上传附件（多文件上传）</p>
<p>​        // 然后通过循环分别处理每一个上传的文件</p>
<p>​        Part part = request.getPart(“photo”);</p>
<p>​        if (part != null &amp;&amp; part.getSubmittedFileName().length() &gt; 0) {</p>
<p>​            // 用ServletContext对象的getRealPath()方法获得上传文件夹的绝对路径</p>
<p>​            String savePath = request.getServletContext().getRealPath(“/upload”);</p>
<p>​            // Servlet 3.1规范中可以用Part对象的getSubmittedFileName()方法获得上传的文件名</p>
<p>​            // 更好的做法是为上传的文件进行重命名（避免同名文件的相互覆盖）</p>
<p>​            part.write(savePath + “/“ + part.getSubmittedFileName());</p>
<p>​            request.setAttribute(“hint”, “Upload Successfully!”);</p>
<p>​        } else {</p>
<p>​            request.setAttribute(“hint”, “Upload failed!”);</p>
<p>​        }</p>
<p>​        // 跳转回到上传页面</p>
<p>​        request.getRequestDispatcher(“index.jsp”).forward(request, response);</p>
<p>​    }</p>
<p>}</p>
<h3 id="服务器收到用户提交的表单数据，到底是调用Servlet的doGet-还是doPost-方法？"><a href="#服务器收到用户提交的表单数据，到底是调用Servlet的doGet-还是doPost-方法？" class="headerlink" title="服务器收到用户提交的表单数据，到底是调用Servlet的doGet()还是doPost()方法？"></a>服务器收到用户提交的表单数据，到底是调用Servlet的doGet()还是doPost()方法？</h3><p> 答：HTML的<form>元素有一个method属性，用来指定提交表单的方式，其值可以是get或post。我们自定义的Servlet一般情况下会重写doGet()或doPost()两个方法之一或全部，如果是GET请求就调用doGet()方法，如果是POST请求就调用doPost()方法，那为什么为什么这样呢？我们自定义的Servlet通常继承自HttpServlet，HttpServlet继承自GenericServlet并重写了其中的service()方法，这个方法是Servlet接口中定义的。HttpServlet重写的service()方法会先获取用户请求的方法，然后根据请求方法调用doGet()、doPost()、doPut()、doDelete()等方法，如果在自定义Servlet中重写了这些方法，那么显然会调用重写过的（自定义的）方法，这显然是对模板方法模式的应用（如果不理解，请参考阎宏博士的《Java与模式》一书的第37章）。当然，自定义Servlet中也可以直接重写service()方法，那么不管是哪种方式的请求，都可以通过自己的代码进行处理，这对于不区分请求方法的场景比较合适。</form></p>
<p>117、JSP中的静态包含和动态包含有什么区别？<br>答：静态包含是通过JSP的include指令包含页面，动态包含是通过JSP标准动作jsp:forward包含页面。静态包含是编译时包含，如果包含的页面不存在则会产生编译错误，而且两个页面的”contentType”属性应保持一致，因为两个页面会合二为一，只产生一个class文件，因此被包含页面发生的变动再包含它的页面更新前不会得到更新。动态包含是运行时包含，可以向被包含的页面传递参数，包含页面和被包含页面是独立的，会编译出两个class文件，如果被包含的页面不存在，不会产生编译错误，也不影响页面其他部分的执行。代码如下所示：</p>
<p>&lt;%– 静态包含 –%&gt;</p>
<p>&lt;%@ include file=”…” %&gt;</p>
<p>&lt;%– 动态包含 –%&gt;</p>
<p>&lt;jsp:include page=”…”&gt;</p>
<p>​    &lt;jsp:param name=”…” value=”…” /&gt;</p>
<p>/jsp:include</p>
<h3 id="Servlet中如何获取用户提交的查询参数或表单数据？"><a href="#Servlet中如何获取用户提交的查询参数或表单数据？" class="headerlink" title="Servlet中如何获取用户提交的查询参数或表单数据？"></a>Servlet中如何获取用户提交的查询参数或表单数据？</h3><p>答：可以通过请求对象（HttpServletRequest）的getParameter()方法通过参数名获得参数值。如果有包含多个值的参数（例如复选框），可以通过请求对象的getParameterValues()方法获得。当然也可以通过请求对象的getParameterMap()获得一个参数名和参数值的映射（Map）。</p>
<p>119、Servlet中如何获取用户配置的初始化参数以及服务器上下文参数？<br>答：可以通过重写Servlet接口的init(ServletConfig)方法并通过ServletConfig对象的getInitParameter()方法来获取Servlet的初始化参数。可以通过ServletConfig对象的getServletContext()方法获取ServletContext对象，并通过该对象的getInitParameter()方法来获取服务器上下文参数。当然，ServletContext对象也在处理用户请求的方法（如doGet()方法）中通过请求对象的getServletContext()方法来获得。</p>
<h3 id="如何设置请求的编码以及响应内容的类型？"><a href="#如何设置请求的编码以及响应内容的类型？" class="headerlink" title="如何设置请求的编码以及响应内容的类型？"></a>如何设置请求的编码以及响应内容的类型？</h3><p>答：通过请求对象（ServletRequest）的setCharacterEncoding(String)方法可以设置请求的编码，其实要彻底解决乱码问题就应该让页面、服务器、请求和响应、Java程序都使用统一的编码，最好的选择当然是UTF-8；通过响应对象（ServletResponse）的setContentType(String)方法可以设置响应内容的类型，当然也可以通过HttpServletResponsed对象的setHeader(String, String)方法来设置。</p>
<p>说明：现在如果还有公司在面试的时候问JSP的声明标记、表达式标记、小脚本标记这些内容的话，这样的公司也不用去了，其实JSP内置对象、JSP指令这些东西基本上都可以忘却了，关于Java Web开发的相关知识，可以看一下我的《Servlet&amp;JSP思维导图》，上面有完整的知识点的罗列。想了解如何实现自定义MVC框架的，可以看一下我的《Java Web自定义MVC框架详解》。</p>
<h3 id="阐述Servlet和CGI的区别"><a href="#阐述Servlet和CGI的区别" class="headerlink" title="阐述Servlet和CGI的区别?"></a>阐述Servlet和CGI的区别?</h3><p>答：Servlet与CGI的区别在于Servlet处于服务器进程中，它通过多线程方式运行其service()方法，一个实例可以服务于多个请求，并且其实例一般不会销毁，而CGI对每个请求都产生新的进程，服务完成后就销毁，所以效率上低于Servlet。</p>
<p>补充：Sun Microsystems公司在1996年发布Servlet技术就是为了和CGI进行竞争，Servlet是一个特殊的Java程序，一个基于Java的Web应用通常包含一个或多个Servlet类。Servlet不能够自行创建并执行，它是在Servlet容器中运行的，容器将用户的请求传递给Servlet程序，并将Servlet的响应回传给用户。通常一个Servlet会关联一个或多个JSP页面。以前CGI经常因为性能开销上的问题被诟病，然而Fast CGI早就已经解决了CGI效率上的问题，所以面试的时候大可不必信口开河的诟病CGI，事实上有很多你熟悉的网站都使用了CGI技术。</p>
<h3 id="什么是Servlet？"><a href="#什么是Servlet？" class="headerlink" title="什么是Servlet？"></a>什么是Servlet？</h3><p>Servlet是用来处理客户端请求并产生动态网页内容的Java类。Servlet主要是用来处理或者是存储HTML表单提交的数据，产生动态内容，在无状态的HTTP协议下管理状态信息。</p>
<h3 id="Servlet接口中有哪些方法？"><a href="#Servlet接口中有哪些方法？" class="headerlink" title="Servlet接口中有哪些方法？"></a>Servlet接口中有哪些方法？</h3><p>答：Servlet接口定义了5个方法，其中前三个方法与Servlet生命周期相关： </p>
<ul>
<li>void init(ServletConfig config) throws ServletException </li>
<li>void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException </li>
<li>void destory() </li>
<li>java.lang.String getServletInfo() </li>
<li>ServletConfig getServletConfig()</li>
</ul>
<p>Web容器加载Servlet并将其实例化后，Servlet生命周期开始，容器运行其init()方法进行Servlet的初始化；请求到达时调用Servlet的service()方法，service()方法会根据需要调用与请求对应的doGet或doPost等方法；当服务器关闭或项目被卸载时服务器会将Servlet实例销毁，此时会调用Servlet的destroy()方法。</p>
<h3 id="说一下Servlet的体系结构。"><a href="#说一下Servlet的体系结构。" class="headerlink" title="说一下Servlet的体系结构。"></a>说一下Servlet的体系结构。</h3><p>所有的Servlet都必须要实现的核心的接口是javax.servlet.Servlet。每一个Servlet都必须要直接或者是间接实现这个接口，或者是继承javax.servlet.GenericServlet或者javax.servlet.http.HTTPServlet。最后，Servlet使用多线程可以并行的为多个请求服务。</p>
<h3 id="Applet和Servlet有什么区别？"><a href="#Applet和Servlet有什么区别？" class="headerlink" title="Applet和Servlet有什么区别？"></a>Applet和Servlet有什么区别？</h3><p>Applet是运行在客户端主机的浏览器上的客户端Java程序。而Servlet是运行在web服务器上的服务端的组件。applet可以使用用户界面类，而Servlet没有用户界面，相反，Servlet是等待客户端的HTTP请求，然后为请求产生响应。</p>
<h3 id="GenericServlet和HttpServlet有什么区别？"><a href="#GenericServlet和HttpServlet有什么区别？" class="headerlink" title="GenericServlet和HttpServlet有什么区别？"></a>GenericServlet和HttpServlet有什么区别？</h3><p>GenericServlet类实现了Servlet和ServletConfig接口。实现了除了service()之外的其他方法，在创建Servlet对象时，可以继承GenericServlet类来简化程序的代码，但需要实现service()方法。</p>
<p>HttpServlet类继承了GeneriServlet类，为实际开发中大多数用Servlet处理 HTTP请求的应用灵活的方法。</p>
<h3 id="解释下Servlet的生命周期。"><a href="#解释下Servlet的生命周期。" class="headerlink" title="解释下Servlet的生命周期。"></a>解释下Servlet的生命周期。</h3><p>对每一个客户端的请求，Servlet引擎载入Servlet，调用它的init()方法，完成Servlet的初始化。然后，Servlet对象通过为每一个请求单独调用service()方法来处理所有随后来自客户端的请求，最后，调用Servlet(译者注：这里应该是Servlet而不是server)的destroy()方法把Servlet删除掉。</p>
<h3 id="doGet-方法和doPost-方法有什么区别？"><a href="#doGet-方法和doPost-方法有什么区别？" class="headerlink" title="doGet()方法和doPost()方法有什么区别？"></a>doGet()方法和doPost()方法有什么区别？</h3><p>doGet：GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制，进而限制了用在客户端请求的参数值的数目。并且请求中的参数值是可见的，因此，敏感信息不能用这种方式传递。</p>
<p>doPOST：POST方法通过把请求参数值放在请求体中来克服GET方法的限制，因此，可以发送的参数的数目是没有限制的。最后，通过POST请求传递的敏感信息对外部客户端是不可见的。</p>
<h3 id="什么是Web应用程序？"><a href="#什么是Web应用程序？" class="headerlink" title="什么是Web应用程序？"></a>什么是Web应用程序？</h3><p>Web应用程序是对Web或者是应用服务器的动态扩展。有两种类型的Web应用：面向表现的和面向服务的。面向表现的Web应用程序会产生包含了很多种标记语言和动态内容的交互的web页面作为对请求的响应。而面向服务的Web应用实现了Web服务的端点(endpoint)。一般来说，一个Web应用可以看成是一组安装在服务器URL名称空间的特定子集下面的Servlet的集合。</p>
<h3 id="什么是服务端包含-Server-Side-Include-？"><a href="#什么是服务端包含-Server-Side-Include-？" class="headerlink" title="什么是服务端包含(Server Side Include)？"></a>什么是服务端包含(Server Side Include)？</h3><p>服务端包含(SSI)是一种简单的解释型服务端脚本语言，大多数时候仅用在Web上，用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候，Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。</p>
<h3 id="什么是Servlet链-Servlet-Chaining-？"><a href="#什么是Servlet链-Servlet-Chaining-？" class="headerlink" title="什么是Servlet链(Servlet Chaining)？"></a>什么是Servlet链(Servlet Chaining)？</h3><p>Servlet链是把一个Servlet的输出发送给另一个Servlet的方法。第二个Servlet的输出可以发送给第三个Servlet，依次类推。链条上最后一个Servlet负责把响应发送给客户端。</p>
<h3 id="如何知道是哪一个客户端的机器正在请求你的Servlet？"><a href="#如何知道是哪一个客户端的机器正在请求你的Servlet？" class="headerlink" title="如何知道是哪一个客户端的机器正在请求你的Servlet？"></a>如何知道是哪一个客户端的机器正在请求你的Servlet？</h3><p>ServletRequest类可以找出客户端机器的IP地址或者是主机名。getRemoteAddr()方法获取客户端主机的IP地址，getRemoteHost()可以获取主机名。看下这里的例子。</p>
<h3 id="HTTP响应的结构是怎么样的？"><a href="#HTTP响应的结构是怎么样的？" class="headerlink" title="HTTP响应的结构是怎么样的？"></a>HTTP响应的结构是怎么样的？</h3><p>HTTP响应由三个部分组成：</p>
<p>状态码(Status Code)：描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下，状态码可用来找出失败的原因。如果Servlet没有返回状态码，默认会返回成功的状态码HttpServletResponse.SC_OK。</p>
<p>HTTP头部(HTTP Header)：它们包含了更多关于响应的信息。比如：头部可以指定认为响应过期的过期日期，或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet中检索HTTP的头部看这里。</p>
<p>主体(Body)：它包含了响应的内容。它可以包含HTML代码，图片，等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。</p>
<h3 id="什么是cookie？session和cookie有什么区别？"><a href="#什么是cookie？session和cookie有什么区别？" class="headerlink" title="什么是cookie？session和cookie有什么区别？"></a>什么是cookie？session和cookie有什么区别？</h3><p>cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候，同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别：</p>
<p>无论客户端浏览器做怎么样的设置，session都应该能正常工作。客户端可以选择禁用cookie，但是，session仍然是能够工作的，因为客户端无法禁用服务端的session。</p>
<p>在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象，cookie只能存储String类型的对象。</p>
<h3 id="浏览器和Servlet通信使用的是什么协议？"><a href="#浏览器和Servlet通信使用的是什么协议？" class="headerlink" title="浏览器和Servlet通信使用的是什么协议？"></a>浏览器和Servlet通信使用的是什么协议？</h3><p>浏览器和Servlet通信使用的是HTTP协议。</p>
<h3 id="什么是HTTP隧道？"><a href="#什么是HTTP隧道？" class="headerlink" title="什么是HTTP隧道？"></a>什么是HTTP隧道？</h3><p>HTTP隧道是一种利用HTTP或者是HTTPS把多种网络协议封装起来进行通信的技术。因此，HTTP协议扮演了一个打通用于通信的网络协议的管道的包装器的角色。把其他协议的请求掩盖成HTTP的请求就是HTTP隧道。</p>
<h3 id="sendRedirect-和forward-方法有什么区别？"><a href="#sendRedirect-和forward-方法有什么区别？" class="headerlink" title="sendRedirect()和forward()方法有什么区别？"></a>sendRedirect()和forward()方法有什么区别？</h3><p>sendRedirect()方法会创建一个新的请求，而forward()方法只是把请求转发到一个新的目标上。重定向(redirect)以后，之前请求作用域范围以内的对象就失效了，因为会产生一个新的请求，而转发(forwarding)以后，之前请求作用域范围以内的对象还是能访问的。一般认为sendRedirect()比forward()要慢。</p>
<h3 id="什么是URL编码和URL解码？"><a href="#什么是URL编码和URL解码？" class="headerlink" title="什么是URL编码和URL解码？"></a>什么是URL编码和URL解码？</h3><p>URL编码是负责把URL里面的空格和其他的特殊字符替换成对应的十六进制表示，反之就是解码。</p>
<h3 id="转发（forward）和重定向（redirect）的区别？"><a href="#转发（forward）和重定向（redirect）的区别？" class="headerlink" title="转发（forward）和重定向（redirect）的区别？"></a>转发（forward）和重定向（redirect）的区别？</h3><p>答：forward是容器中控制权的转向，是服务器请求资源，服务器直接访问目标地址的URL，把那个URL 的响应内容读取过来，然后把这些内容再发给浏览器，浏览器根本不知道服务器发送的内容是从哪儿来的，所以它的地址栏中还是原来的地址。redirect就是服务器端根据逻辑，发送一个状态码，告诉浏览器重新去请求那个地址，因此从浏览器的地址栏中可以看到跳转后的链接地址，很明显redirect无法访问到服务器保护起来资源，但是可以从一个网站redirect到其他网站。forward更加高效，所以在满足需要时尽量使用forward（通过调用RequestDispatcher对象的forward()方法，该对象可以通过ServletRequest对象的getRequestDispatcher()方法获得），并且这样也有助于隐藏实际的链接；在有些情况下，比如需要访问一个其它服务器上的资源，则必须使用重定向（通过HttpServletResponse对象调用其sendRedirect()方法实现）。</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-序列化相关/">
        Java 面试题系列篇-序列化相关
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="序列化相关"><a href="#序列化相关" class="headerlink" title="序列化相关"></a>序列化相关</h2><h3 id="解释下Marshalling和demarshalling。"><a href="#解释下Marshalling和demarshalling。" class="headerlink" title="解释下Marshalling和demarshalling。"></a>解释下Marshalling和demarshalling。</h3><p>当应用程序希望把内存对象跨网络传递到另一台主机或者是持久化到存储的时候，就必须要把对象在内存里面的表示转化成合适的格式。这个过程就叫做Marshalling，反之就是demarshalling。</p>
<h3 id="解释下Serialization和Deserialization。"><a href="#解释下Serialization和Deserialization。" class="headerlink" title="解释下Serialization和Deserialization。"></a>解释下Serialization和Deserialization。</h3><p>Java提供了一种叫做对象序列化的机制，他把对象表示成一连串的字节，里面包含了对象的数据，对象的类型信息，对象内部的数据的类型信息等等。因此，序列化可以看成是为了把对象存储在磁盘上或者是从磁盘上读出来并重建对象而把对象扁平化的一种方式。反序列化是把对象从扁平状态转化成活动对象的相反的步骤。</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
    <div class='post-wrapper'>
      <article class="post reveal">
  <section class="meta">
    
    <h2 class="title">
      <a href="/2018/10/20/Java-面试题系列篇-基础算法/">
        Java 面试题系列篇-基础算法
      </a>
    </h2>
    
    <time>
      Oct 20, 2018
    </time>
		
  </section>
  <section class="article typo">
	  <h2 id="基础算法"><a href="#基础算法" class="headerlink" title="基础算法"></a>基础算法</h2><h3 id="用Java写一个冒泡排序。"><a href="#用Java写一个冒泡排序。" class="headerlink" title="用Java写一个冒泡排序。"></a>用Java写一个冒泡排序。</h3><p>答：冒泡排序几乎是个程序员都写得出来，但是面试的时候如何写一个逼格高的冒泡排序却不是每个人都能做到，下面提供一个参考代码：</p>
<p>import java.util.Comparator;</p>
<p>/**</p>
<ul>
<li>排序器接口(策略模式: 将算法封装到具有共同接口的独立的类中使得它们可以相互替换)</li>
<li><p>@author骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public interface Sorter {</p>
<p>   /**</p>
<p>​    * 排序</p>
<p>​    * @param list 待排序的数组</p>
<p>​    */</p>
<p>   public &lt;T extends Comparable<t>&gt; void sort(T[] list);</t></p>
<p>   /**</p>
<p>​    * 排序</p>
<p>​    * @param list 待排序的数组</p>
<p>​    * @param comp 比较两个对象的比较器</p>
<p>​    */</p>
<p>   public <t> void sort(T[] list, Comparator<t> comp);</t></t></p>
<p>}</p>
<p>import java.util.Comparator;</p>
<p>/**</p>
<ul>
<li>冒泡排序</li>
<li></li>
<li><p>@author骆昊</p>
<p>*</p>
<p>*/</p>
</li>
</ul>
<p>public class BubbleSorter implements Sorter {</p>
<p>​    @Override</p>
<p>​    public &lt;T extends Comparable<t>&gt; void sort(T[] list) {</t></p>
<p>​        boolean swapped = true;</p>
<p>​        for (int i = 1, len = list.length; i &lt; len &amp;&amp; swapped; ++i) {</p>
<p>​            swapped = false;</p>
<p>​            for (int j = 0; j &lt; len - i; ++j) {</p>
<p>​                if (list[j].compareTo(list[j + 1]) &gt; 0) {</p>
<p>​                    T temp = list[j];</p>
<p>​                    list[j] = list[j + 1];</p>
<p>​                    list[j + 1] = temp;</p>
<p>​                    swapped = true;</p>
<p>​                }</p>
<p>​            }</p>
<p>​        }</p>
<p>​    }</p>
<p>​    @Override</p>
<p>​    public <t> void sort(T[] list, Comparator<t> comp) {</t></t></p>
<p>​        boolean swapped = true;</p>
<p>​        for (int i = 1, len = list.length; i &lt; len &amp;&amp; swapped; ++i) {</p>
<p>​            swapped = false;</p>
<p>​            for (int j = 0; j &lt; len - i; ++j) {</p>
<p>​                if (comp.compare(list[j], list[j + 1]) &gt; 0) {</p>
<p>​                    T temp = list[j];</p>
<p>​                    list[j] = list[j + 1];</p>
<p>​                    list[j + 1] = temp;</p>
<p>​                    swapped = true;</p>
<p>​                }</p>
<p>​            }</p>
<p>​        }</p>
<p>​    }</p>
<p>}</p>
<h3 id="用Java写一个折半查找。"><a href="#用Java写一个折半查找。" class="headerlink" title="用Java写一个折半查找。"></a>用Java写一个折半查找。</h3><p>答：折半查找，也称二分查找、二分搜索，是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜素过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。如果在某一步骤数组已经为空，则表示找不到指定的元素。这种搜索算法每一次比较都使搜索范围缩小一半，其时间复杂度是O(logN)。</p>
<p>import java.util.Comparator;</p>
<p>public class MyUtil {</p>
<p>   public static &lt;T extends Comparable<t>&gt; int binarySearch(T[] x, T key) {</t></p>
<p>​      return binarySearch(x, 0, x.length- 1, key);</p>
<p>   }</p>
<p>   // 使用循环实现的二分查找</p>
<p>   public static <t> int binarySearch(T[] x, T key, Comparator<t> comp) {</t></t></p>
<p>​      int low = 0;</p>
<p>​      int high = x.length - 1;</p>
<p>​      while (low &lt;= high) {</p>
<p>​          int mid = (low + high) &gt;&gt;&gt; 1;</p>
<p>​          int cmp = comp.compare(x[mid], key);</p>
<p>​          if (cmp &lt; 0) {</p>
<p>​            low= mid + 1;</p>
<p>​          }</p>
<p>​          else if (cmp &gt; 0) {</p>
<p>​            high= mid - 1;</p>
<p>​          }</p>
<p>​          else {</p>
<p>​            return mid;</p>
<p>​          }</p>
<p>​      }</p>
<p>​      return -1;</p>
<p>   }</p>
<p>   // 使用递归实现的二分查找</p>
<p>   private static&lt;T extends Comparable<t>&gt; int binarySearch(T[] x, int low, int high, T key) {</t></p>
<p>​      if(low &lt;= high) {</p>
<p>​        int mid = low + ((high -low) &gt;&gt; 1);</p>
<p>​        if(key.compareTo(x[mid])== 0) {</p>
<p>​           return mid;</p>
<p>​        }</p>
<p>​        else if(key.compareTo(x[mid])&lt; 0) {</p>
<p>​           return binarySearch(x,low, mid - 1, key);</p>
<p>​        }</p>
<p>​        else {</p>
<p>​           return binarySearch(x,mid + 1, high, key);</p>
<p>​        }</p>
<p>​      }</p>
<p>​      return -1;</p>
<p>   }</p>
<p>}</p>
<p>说明：上面的代码中给出了折半查找的两个版本，一个用递归实现，一个用循环实现。需要注意的是计算中间位置时不应该使用(high+ low) / 2的方式，因为加法运算可能导致整数越界，这里应该使用以下三种方式之一：low + (high - low) / 2或low + (high – low) &gt;&gt; 1或(low + high) &gt;&gt;&gt; 1（&gt;&gt;&gt;是逻辑右移，是不带符号位的右移）</p>
<h3 id="什么是Java虚拟机？为什么Java被称作是“平台无关的编程语言”？"><a href="#什么是Java虚拟机？为什么Java被称作是“平台无关的编程语言”？" class="headerlink" title="什么是Java虚拟机？为什么Java被称作是“平台无关的编程语言”？"></a>什么是Java虚拟机？为什么Java被称作是“平台无关的编程语言”？</h3><p>Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。</p>
<p>Java被设计成允许应用程序可以运行在任意的平台，而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能，因为它知道底层硬件平台的指令长度和其他特性。</p>
<h3 id="”static”关键字是什么意思？Java中是否可以覆盖-override-一个private或者是static的方法？"><a href="#”static”关键字是什么意思？Java中是否可以覆盖-override-一个private或者是static的方法？" class="headerlink" title="”static”关键字是什么意思？Java中是否可以覆盖(override)一个private或者是static的方法？"></a>”static”关键字是什么意思？Java中是否可以覆盖(override)一个private或者是static的方法？</h3><p>“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。</p>
<p>Java中static方法不能被覆盖，因为方法覆盖是基于运行时动态绑定的，而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关，所以概念上不适用。</p>
<p>java中也不可以覆盖private的方法，因为private修饰的变量和方法只能在当前类中使用，如果是其他的类继承当前类是不能访问到private变量或方法的，当然也不能覆盖。</p>
<h3 id="是否可以在static环境中访问非static变量？"><a href="#是否可以在static环境中访问非static变量？" class="headerlink" title="是否可以在static环境中访问非static变量？"></a>是否可以在static环境中访问非static变量？</h3><p>static变量在Java中是属于类的，它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候，会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量，编译器会报错，因为这些变量还没有被创建出来，还没有跟任何实例关联上。</p>
<h3 id="Java支持的数据类型有哪些？什么是自动拆装箱？"><a href="#Java支持的数据类型有哪些？什么是自动拆装箱？" class="headerlink" title="Java支持的数据类型有哪些？什么是自动拆装箱？"></a>Java支持的数据类型有哪些？什么是自动拆装箱？</h3><p>Java语言支持的8种基本数据类型是：</p>
<p>byte</p>
<p>short</p>
<p>int</p>
<p>long</p>
<p>float</p>
<p>double</p>
<p>boolean</p>
<p>char</p>
<p>自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如：把int转化成Integer，double转化成Double，等等。反之就是自动拆箱。</p>
<p>Java支持的数据类型包括两种：一种是基本数据类型，包含byte，char,short, boolean ,int , long, float,double;另一种是引用类型：如String等，其实是对象的引用，JVM中虚拟栈中存的是对象的地址，创建的对象实质在堆中，通过地址来找到堆中的对象的过程，即为引用类型。自动装箱就是Java编译器在基本数据类型和对应的对象包装类型间的转化，即int转化为Integer,自动拆箱是Integer调用其方法将其转化为int的过程</p>
<h3 id="Java中的方法覆盖-Overriding-和方法重载-Overloading-是什么意思？"><a href="#Java中的方法覆盖-Overriding-和方法重载-Overloading-是什么意思？" class="headerlink" title="Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思？"></a>Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思？</h3><p>Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对，方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名，参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。</p>
<h3 id="Java中，什么是构造方法？什么是构造方法重载？什么是复制构造方法？"><a href="#Java中，什么是构造方法？什么是构造方法重载？什么是复制构造方法？" class="headerlink" title="Java中，什么是构造方法？什么是构造方法重载？什么是复制构造方法？"></a>Java中，什么是构造方法？什么是构造方法重载？什么是复制构造方法？</h3><p>当新对象被创建的时候，构造方法会被调用。每一个类都有构造方法。在程序员没有给类提供构造方法的情况下，Java编译器会为这个类创建一个默认的构造方法。</p>
<p>Java中构造方法重载和方法重载很相似。可以为一个类创建多个构造方法。每一个构造方法必须有它自己唯一的参数列表。</p>
<p>Java不支持像C++中那样的复制构造方法，这个不同点是因为如果你不自己写构造方法的情况下，Java不会创建默认的复制构造方法。</p>
<h3 id="Java支持多继承么？"><a href="#Java支持多继承么？" class="headerlink" title="Java支持多继承么？"></a>Java支持多继承么？</h3><p>Java中类不支持多继承，只支持单继承（即一个类只有一个父类）。 但是java中的接口支持多继承，，即一个子接口可以有多个父接口。（接口的作用是用来扩展对象的功能，一个子接口继承多个父接口，说明子接口扩展了多个功能，当类实现接口时，类就扩展了相应的功能）。</p>
<h3 id="接口和抽象类的区别是什么？"><a href="#接口和抽象类的区别是什么？" class="headerlink" title="接口和抽象类的区别是什么？"></a>接口和抽象类的区别是什么？</h3><p>Java提供和支持创建抽象类和接口。它们的实现有共同点，不同点在于：</p>
<p>接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。</p>
<p>类可以实现很多个接口，但是只能继承一个抽象类</p>
<p>类可以不实现抽象类和接口声明的所有方法，当然，在这种情况下，类也必须得声明成是抽象的。</p>
<p>抽象类可以在不提供接口方法实现的情况下实现接口。</p>
<p>Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。</p>
<p>Java接口中的成员函数默认是public的。抽象类的成员函数可以是private，protected或者是public。</p>
<p>接口是绝对抽象的，不可以被实例化。抽象类也不可以被实例化，但是，如果它包含main方法的话是可以被调用的。</p>
<p>也可以参考JDK8中抽象类和接口的区别</p>
<h3 id="什么是值传递和引用传递？"><a href="#什么是值传递和引用传递？" class="headerlink" title="什么是值传递和引用传递？"></a>什么是值传递和引用传递？</h3><p>值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.</p>
<p>引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。</p>
<p>一般认为,java内的传递都是值传递. java中实例对象的传递是引用传递  </p>
<h3 id="进程和线程的区别是什么？"><a href="#进程和线程的区别是什么？" class="headerlink" title="进程和线程的区别是什么？"></a>进程和线程的区别是什么？</h3><p>进程是执行着的应用程序，而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。</p>
<p>线程的划分小于进程，线程隶属于某个进程。 进程是程序的一种动态形式，是CPU、内存等资源占用的基本单位，而线程是不能占有这些资源的。 进程之间相互独立，通信比较困难，而线程之间共享一块内存区域，通信比较方便。 进程在执行的过程中，包含比较固定的入口，执行顺序，出口，而线程的这些过程会被应用程序所控制。</p>
<h3 id="创建线程有几种不同的方式？你喜欢哪一种？为什么？"><a href="#创建线程有几种不同的方式？你喜欢哪一种？为什么？" class="headerlink" title="创建线程有几种不同的方式？你喜欢哪一种？为什么？"></a>创建线程有几种不同的方式？你喜欢哪一种？为什么？</h3><p>有4种方式可以用来创建线程：</p>
<p>继承Thread类</p>
<p>实现Runnable接口</p>
<p>应用程序可以使用Executor框架来创建线程池</p>
<p>实现Runnable接口这种方式更受欢迎，因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下，这需要多继承（而Java不支持多继承），只能实现接口。同时，线程池也是非常高效的，很容易实现和使用。</p>
<p>还有一种方式是实现Callable接口。</p>
<h3 id="概括的解释下线程的几种可用状态。"><a href="#概括的解释下线程的几种可用状态。" class="headerlink" title="概括的解释下线程的几种可用状态。"></a>概括的解释下线程的几种可用状态。</h3><p>\1. 新建( new )：新创建了一个线程对象。</p>
<p>\2. 可运行( runnable )：线程对象创建后，其他线程(比如 main 线程）调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中，等待被线程调度选中，获 取 cpu 的使用权 。</p>
<p>\3. 运行( running )：可运行状态( runnable )的线程获得了 cpu 时间片（ timeslice ） ，执行程序代码。</p>
<p>\4. 阻塞( block )：阻塞状态是指线程因为某种原因放弃了 cpu 使用权，也即让出了 cpu timeslice ，暂时停止运行。直到线程进入可运行( runnable )状态，才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种：</p>
<p>(一). 等待阻塞：运行( running )的线程执行 o . wait ()方法， JVM 会把该线程放 入等待队列( waitting queue )中。</p>
<p>(二). 同步阻塞：运行( running )的线程在获取对象的同步锁时，若该同步锁 被别的线程占用，则 JVM 会把该线程放入锁池( lock pool )中。</p>
<p>(三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法，或者发出了 I / O 请求时， JVM 会把该线程置为阻塞状态。            当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时，线程重新转入可运行( runnable )状态。</p>
<p>\5. 死亡( dead )：线程 run ()、 main () 方法执行结束，或者因异常退出了 run ()方法，则该线程结束生命周期。死亡的线程不可再次复生。</p>
<h3 id="同步方法和同步代码块的区别是什么？"><a href="#同步方法和同步代码块的区别是什么？" class="headerlink" title="同步方法和同步代码块的区别是什么？"></a>同步方法和同步代码块的区别是什么？</h3><p>区别：</p>
<p>同步方法默认用this或者当前类class对象作为锁；</p>
<p>同步代码块可以选择以什么来加锁，比同步方法要更细颗粒度，我们可以选择只同步会发生同步问题的部分代码而不是整个方法；</p>
<p>同步方法使用关键字 synchronized修饰方法，而同步代码块主要是修饰需要进行同步的代码，用   synchronized（object）{代码内容}进行修饰；</p>
<h3 id="在监视器-Monitor-内部，是如何做线程同步的？程序应该做哪种级别的同步？"><a href="#在监视器-Monitor-内部，是如何做线程同步的？程序应该做哪种级别的同步？" class="headerlink" title="在监视器(Monitor)内部，是如何做线程同步的？程序应该做哪种级别的同步？"></a>在监视器(Monitor)内部，是如何做线程同步的？程序应该做哪种级别的同步？</h3><p>监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块，确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。</p>
<h3 id="什么是死锁-deadlock-？"><a href="#什么是死锁-deadlock-？" class="headerlink" title="什么是死锁(deadlock)？"></a>什么是死锁(deadlock)？</h3><p>所谓死锁是指多个进 程因竞争资源而造成的一种僵局（互相等待），若无外力作用，这些进程都将无法向前推进。死锁产生的4个必要条件：</p>
<p>互斥条件：进程要求对所分配的资源（如打印机）进行排他性控制，即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源，则请求进程只能等待。</p>
<p>不剥夺条件：进程所获得的资源在未使用完毕之前，不能被其他进程强行夺走，即只能 由获得该资源的进程自己来释放（只能是主动释放)。</p>
<p>请求和保持条件：进程已经保持了至少一个资源，但又提出了新的资源请求，而该资源 已被其他进程占有，此时请求进程被阻塞，但对自己已获得的资源保持不放。</p>
<p>循环等待条件：存在一种进程资源的循环等待链，链中每一个进程已获得的资源同时被 链中下一个进程所请求。</p>
<h3 id="如何确保N个线程可以访问N个资源同时又不导致死锁？"><a href="#如何确保N个线程可以访问N个资源同时又不导致死锁？" class="headerlink" title="如何确保N个线程可以访问N个资源同时又不导致死锁？"></a>如何确保N个线程可以访问N个资源同时又不导致死锁？</h3><p>使用多线程的时候，一种非常简单的避免死锁的方式就是：指定获取锁的顺序，并强制线程按照指定的顺序获取锁。因此，如果所有的线程都是以同样的顺序加锁和释放锁，就不会出现死锁了。</p>
<h3 id="Java集合类框架的基本接口有哪些？"><a href="#Java集合类框架的基本接口有哪些？" class="headerlink" title="Java集合类框架的基本接口有哪些？"></a>Java集合类框架的基本接口有哪些？</h3><p>集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许重复的键，有些不允许。</p>
<p>Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有：</p>
<p>Collection：代表一组对象，每一个对象都是它的子元素。</p>
<p>Set：不包含重复元素的Collection。</p>
<p>List：有顺序的collection，并且可以包含重复元素。</p>
<p>Map：可以把键(key)映射到值(value)的对象，键不能重复。</p>
<h3 id="为什么集合类没有实现Cloneable和Serializable接口？"><a href="#为什么集合类没有实现Cloneable和Serializable接口？" class="headerlink" title="为什么集合类没有实现Cloneable和Serializable接口？"></a>为什么集合类没有实现Cloneable和Serializable接口？</h3><p>克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此，应该由集合类的具体实现来决定如何被克隆或者是序列化。</p>
<h3 id="什么是迭代器-Iterator-？"><a href="#什么是迭代器-Iterator-？" class="headerlink" title="什么是迭代器(Iterator)？"></a>什么是迭代器(Iterator)？</h3><p>Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的</p>
<p>迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的</p>
<p>remove(Object Obj)删除，可以通过迭代器的remove()方法删除。</p>
<h3 id="Iterator和ListIterator的区别是什么？"><a href="#Iterator和ListIterator的区别是什么？" class="headerlink" title="Iterator和ListIterator的区别是什么？"></a>Iterator和ListIterator的区别是什么？</h3><p>下面列出了他们的区别：</p>
<p>Iterator可用来遍历Set和List集合，但是ListIterator只能用来遍历List。</p>
<p>Iterator对集合只能是前向遍历，ListIterator既可以前向也可以后向。</p>
<p>ListIterator实现了Iterator接口，并包含其他的功能，比如：增加元素，替换元素，获取前一个和后一个元素的索引，等等。</p>
<h3 id="快速失败-fail-fast-和安全失败-fail-safe-的区别是什么？"><a href="#快速失败-fail-fast-和安全失败-fail-safe-的区别是什么？" class="headerlink" title="快速失败(fail-fast)和安全失败(fail-safe)的区别是什么？"></a>快速失败(fail-fast)和安全失败(fail-safe)的区别是什么？</h3><p>Iterator的安全失败是基于对底层集合做拷贝，因此，它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的，而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常，而安全失败的迭代器永远不会抛出这样的异常。</p>
<h3 id="Java中的HashMap的工作原理是什么？"><a href="#Java中的HashMap的工作原理是什么？" class="headerlink" title="Java中的HashMap的工作原理是什么？"></a>Java中的HashMap的工作原理是什么？</h3><p>Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数，它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候，HashMap会计算key的hash值，然后把键值对存储在集合中合适的索引上。如果key已经存在了，value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity)，负载因子(load factor)和扩容极限(threshold resizing)。</p>
<h3 id="hashCode-和equals-方法的重要性体现在什么地方？"><a href="#hashCode-和equals-方法的重要性体现在什么地方？" class="headerlink" title="hashCode()和equals()方法的重要性体现在什么地方？"></a>hashCode()和equals()方法的重要性体现在什么地方？</h3><p>Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引，当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法，两个不同的键可能会有相同的hash值，因此，可能会被集合认为是相等的。而且，这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。</p>
<h3 id="HashMap和Hashtable有什么区别？"><a href="#HashMap和Hashtable有什么区别？" class="headerlink" title="HashMap和Hashtable有什么区别？"></a>HashMap和Hashtable有什么区别？</h3><p>HashMap和Hashtable都实现了Map接口，因此很多特性非常相似。但是，他们有以下不同点：</p>
<p>HashMap允许键和值是null，而Hashtable不允许键或者值是null。</p>
<p>Hashtable是同步的，而HashMap不是。因此，HashMap更适合于单线程环境，而Hashtable适合于多线程环境。</p>
<p>HashMap提供了可供应用迭代的键的集合，因此，HashMap是快速失败的。另一方面，Hashtable提供了对键的列举(Enumeration)。</p>
<p>一般认为Hashtable是一个遗留的类。</p>
<h3 id="数组-Array-和列表-ArrayList-有什么区别？什么时候应该使用Array而不是ArrayList？"><a href="#数组-Array-和列表-ArrayList-有什么区别？什么时候应该使用Array而不是ArrayList？" class="headerlink" title="数组(Array)和列表(ArrayList)有什么区别？什么时候应该使用Array而不是ArrayList？"></a>数组(Array)和列表(ArrayList)有什么区别？什么时候应该使用Array而不是ArrayList？</h3><p>下面列出了Array和ArrayList的不同点：</p>
<p>Array可以包含基本类型和对象类型，ArrayList只能包含对象类型。</p>
<p>Array大小是固定的，ArrayList的大小是动态变化的。</p>
<p>ArrayList提供了更多的方法和特性，比如：addAll()，removeAll()，iterator()等等。</p>
<p>对于基本类型数据，集合使用自动装箱来减少编码工作量。但是，当处理固定大小的基本数据类型的时候，这种方式相对比较慢。</p>
<h3 id="ArrayList和LinkedList有什么区别？"><a href="#ArrayList和LinkedList有什么区别？" class="headerlink" title="ArrayList和LinkedList有什么区别？"></a>ArrayList和LinkedList有什么区别？</h3><p>ArrayList和LinkedList都实现了List接口，他们有以下的不同点：</p>
<p>ArrayList是基于索引的数据接口，它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应，LinkedList是以元素列表的形式存储它的数据，每一个元素都和它的前一个和后一个元素链接在一起，在这种情况下，查找某个元素的时间复杂度是O(n)。</p>
<p>相对于ArrayList，LinkedList的插入，添加，删除操作速度更快，因为当元素被添加到集合任意位置的时候，不需要像数组那样重新计算大小或者是更新索引。</p>
<p>LinkedList比ArrayList更占内存，因为LinkedList为每一个节点存储了两个引用，一个指向前一个元素，一个指向下一个元素。</p>
<p>也可以参考ArrayList vs. LinkedList。</p>
<h3 id="Comparable和Comparator接口是干什么的？列出它们的区别。"><a href="#Comparable和Comparator接口是干什么的？列出它们的区别。" class="headerlink" title="Comparable和Comparator接口是干什么的？列出它们的区别。"></a>Comparable和Comparator接口是干什么的？列出它们的区别。</h3><p>Java提供了只包含一个compareTo()方法的Comparable接口。这个方法可以个给两个对象排序。具体来说，它返回负数，0，正数来表明已经存在的对象小于，等于，大于输入对象。</p>
<p>Java提供了包含compare()和equals()两个方法的Comparator接口。compare()方法用来给两个输入参数排序，返回负数，0，正数表明第一个参数是小于，等于，大于第二个参数。equals()方法需要一个对象作为参数，它用来决定输入参数是否和comparator相等。只有当输入参数也是一个comparator并且输入参数和当前comparator的排序结果是相同的时候，这个方法才返回true。</p>
<h3 id="什么是Java优先级队列-Priority-Queue-？"><a href="#什么是Java优先级队列-Priority-Queue-？" class="headerlink" title="什么是Java优先级队列(Priority Queue)？"></a>什么是Java优先级队列(Priority Queue)？</h3><p>PriorityQueue是一个基于优先级堆的无界队列，它的元素是按照自然顺序(natural order)排序的。在创建的时候，我们可以给它提供一个负责给元素排序的比较器。PriorityQueue不允许null值，因为他们没有自然顺序，或者说他们没有任何的相关联的比较器。最后，PriorityQueue不是线程安全的，入队和出队的时间复杂度是O(log(n))。</p>
<h3 id="你了解大O符号-big-O-notation-么？你能给出不同数据结构的例子么？"><a href="#你了解大O符号-big-O-notation-么？你能给出不同数据结构的例子么？" class="headerlink" title="你了解大O符号(big-O notation)么？你能给出不同数据结构的例子么？"></a>你了解大O符号(big-O notation)么？你能给出不同数据结构的例子么？</h3><p>大O符号描述了当数据结构里面的元素增加的时候，算法的规模或者是一个渐进上界 。</p>
<p>大O符号也可用来描述其他的行为，比如：内存消耗。因为集合类实际上是数据结构，我们一般使用大O符号基于时间，内存和性能来选择最好的实现。大O符号可以对大量数据的性能给出一个很好的说明。</p>
<h3 id="如何权衡是使用无序的数组还是有序的数组？"><a href="#如何权衡是使用无序的数组还是有序的数组？" class="headerlink" title="如何权衡是使用无序的数组还是有序的数组？"></a>如何权衡是使用无序的数组还是有序的数组？</h3><p>有序数组最大的好处在于查找的时间复杂度是O(log n)，而无序数组是O(n)。有序数组的缺点是插入操作的时间复杂度是O(n)，因为值大的元素需要往后移动来给新元素腾位置。相反，无序数组的插入时间复杂度是常量O(1)。</p>
<h3 id="Java集合类框架的最佳实践有哪些？"><a href="#Java集合类框架的最佳实践有哪些？" class="headerlink" title="Java集合类框架的最佳实践有哪些？"></a>Java集合类框架的最佳实践有哪些？</h3><p>根据应用的需要正确选择要使用的集合的类型对性能非常重要，比如：假如元素的数量是固定的，而且能事先知道，我们就应该用Array而不是ArrayList。</p>
<p>有些集合类允许指定初始容量。因此，如果我们能估计出存储的元素的数目，我们可以设置初始容量来避免重新计算hash值或者是扩容。</p>
<p>为了类型安全，可读性和健壮性的原因总是要使用泛型。同时，使用泛型还可以避免运行时的ClassCastException。</p>
<p>使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。</p>
<p>编程的时候接口优于实现。</p>
<p>底层的集合实际上是空的情况下，返回长度是0的集合或者是数组，不要返回null。</p>
<h3 id="Enumeration接口和Iterator接口的区别有哪些？"><a href="#Enumeration接口和Iterator接口的区别有哪些？" class="headerlink" title="Enumeration接口和Iterator接口的区别有哪些？"></a>Enumeration接口和Iterator接口的区别有哪些？</h3><p>Enumeration速度是Iterator的2倍，同时占用更少的内存。但是，Iterator远远比Enumeration安全，因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时，Iterator允许调用者删除底层集合里面的元素，这对Enumeration来说是不可能的。</p>
<h3 id="HashSet和TreeSet有什么区别？"><a href="#HashSet和TreeSet有什么区别？" class="headerlink" title="HashSet和TreeSet有什么区别？"></a>HashSet和TreeSet有什么区别？</h3><p>HashSet是由一个hash表来实现的，因此，它的元素是无序的。add()，remove()，contains()方法的时间复杂度是O(1)。</p>
<p>另一方面，TreeSet是由一个树形的结构来实现的，它里面的元素是有序的。因此，add()，remove()，contains()方法的时间复杂度是O(logn)。</p>
<h3 id="Java中垃圾回收有什么目的？什么时候进行垃圾回收？"><a href="#Java中垃圾回收有什么目的？什么时候进行垃圾回收？" class="headerlink" title="Java中垃圾回收有什么目的？什么时候进行垃圾回收？"></a>Java中垃圾回收有什么目的？什么时候进行垃圾回收？</h3><p>垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行。</p>
<p>垃圾回收的目的是通过识别并且丢弃应用不再使用的对象来释放和重用资源。</p>
<h3 id="System-gc-和Runtime-gc-会做什么事情？"><a href="#System-gc-和Runtime-gc-会做什么事情？" class="headerlink" title="System.gc()和Runtime.gc()会做什么事情？"></a>System.gc()和Runtime.gc()会做什么事情？</h3><p>这两个方法用来提示JVM要进行垃圾回收。但是，立即开始还是延迟进行垃圾回收是取决于JVM的。</p>
<h3 id="finalize-方法什么时候被调用？析构函数-finalization-的目的是什么？"><a href="#finalize-方法什么时候被调用？析构函数-finalization-的目的是什么？" class="headerlink" title="finalize()方法什么时候被调用？析构函数(finalization)的目的是什么？"></a>finalize()方法什么时候被调用？析构函数(finalization)的目的是什么？</h3><p>垃圾回收器(garbage colector)决定回收某对象时，就会运行该对象的finalize()方法 但是在Java中很不幸，如果内存总是充足的，那么垃圾回收可能永远不会进行，也就是说filalize()可能永远不被执行，显然指望它做收尾工作是靠不住的。 那么finalize()究竟是做什么的呢？它最主要 的用途是回收特殊渠道申请的内存。Java程序有垃圾回收器，所以一般情况下内存问题不用程序员操心。但有一种JNI(Java Native Interface)调用non-Java程序（C或C++），finalize()的工作就是回收这部分的内存。</p>
<h3 id="如果对象的引用被置为null，垃圾收集器是否会立即释放对象占用的内存？"><a href="#如果对象的引用被置为null，垃圾收集器是否会立即释放对象占用的内存？" class="headerlink" title="如果对象的引用被置为null，垃圾收集器是否会立即释放对象占用的内存？"></a>如果对象的引用被置为null，垃圾收集器是否会立即释放对象占用的内存？</h3><p>不会，在下一个垃圾回收周期中，这个对象将是可被回收的。</p>
<h3 id="Java堆的结构是什么样子的？什么是堆中的永久代-Perm-Gen-space"><a href="#Java堆的结构是什么样子的？什么是堆中的永久代-Perm-Gen-space" class="headerlink" title="Java堆的结构是什么样子的？什么是堆中的永久代(Perm Gen space)?"></a>Java堆的结构是什么样子的？什么是堆中的永久代(Perm Gen space)?</h3><p>JVM的堆是运行时数据区，所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。</p>
<p>堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的，不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前，他们会一直占据堆内存空间。</p>
<p>永久代是用于存放静态文件，如Java类、方法等。持久代对垃圾回收没有显著影响，但是有些应用可能动态生成或者调用一些class，例如Hibernate 等，在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类，永久代中一般包含：</p>
<p>类的方法(字节码…)</p>
<p>类名(Sring对象)</p>
<p>.class文件读到的常量信息</p>
<p>class对象相关的对象列表和类型列表 (e.g., 方法对象的array).</p>
<p>JVM创建的内部对象</p>
<p>JIT编译器优化用的信息</p>
<h3 id="串行-serial-收集器和吞吐量-throughput-收集器的区别是什么？"><a href="#串行-serial-收集器和吞吐量-throughput-收集器的区别是什么？" class="headerlink" title="串行(serial)收集器和吞吐量(throughput)收集器的区别是什么？"></a>串行(serial)收集器和吞吐量(throughput)收集器的区别是什么？</h3><p>吞吐量收集器使用并行版本的新生代垃圾收集器，它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。</p>
<h3 id="在Java中，对象什么时候可以被垃圾回收？"><a href="#在Java中，对象什么时候可以被垃圾回收？" class="headerlink" title="在Java中，对象什么时候可以被垃圾回收？"></a>在Java中，对象什么时候可以被垃圾回收？</h3><p>当对象对当前使用这个对象的应用程序变得不可触及的时候，这个对象就可以被回收了。</p>
<h3 id="JVM的永久代中会发生垃圾回收么？"><a href="#JVM的永久代中会发生垃圾回收么？" class="headerlink" title="JVM的永久代中会发生垃圾回收么？"></a>JVM的永久代中会发生垃圾回收么？</h3><p>垃圾回收不会发生在永久代，如果永久代满了或者是超过了临界值，会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息，就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8：从永久代到元数据区</p>
<p>(注：Java8中已经移除了永久代，新加了一个叫做元数据区的native内存区)</p>
<h3 id="Java中的两种异常类型是什么？他们有什么区别？"><a href="#Java中的两种异常类型是什么？他们有什么区别？" class="headerlink" title="Java中的两种异常类型是什么？他们有什么区别？"></a>Java中的两种异常类型是什么？他们有什么区别？</h3><p>Java中有两种异常：受检查的(checked)异常和不受检查的(unchecked)异常。不受检查的异常不需要在方法或者是构造函数上声明，就算方法或者是构造函数的执行可能会抛出这样的异常，并且不受检查的异常可以传播到方法或者是构造函数的外面。相反，受检查的异常必须要用throws语句在方法或者是构造函数上声明。这里有Java异常处理的一些小建议。</p>
<h3 id="Java中Exception和Error有什么区别？"><a href="#Java中Exception和Error有什么区别？" class="headerlink" title="Java中Exception和Error有什么区别？"></a>Java中Exception和Error有什么区别？</h3><p>Exception和Error都是Throwable的子类。Exception用于用户程序可以捕获的异常情况。Error定义了不期望被用户程序捕获的异常。</p>
<h3 id="throw和throws有什么区别？"><a href="#throw和throws有什么区别？" class="headerlink" title="throw和throws有什么区别？"></a>throw和throws有什么区别？</h3><p>throw关键字用来在程序中明确的抛出异常，相反，throws语句用来表明方法不能处理的异常。每一个方法都必须要指定哪些异常不能处理，所以方法的调用者才能够确保处理可能发生的异常，多个异常是用逗号分隔的。</p>
<h3 id="异常处理完成以后，Exception对象会发生什么变化？"><a href="#异常处理完成以后，Exception对象会发生什么变化？" class="headerlink" title="异常处理完成以后，Exception对象会发生什么变化？"></a>异常处理完成以后，Exception对象会发生什么变化？</h3><p>Exception对象会在下一个垃圾回收过程中被回收掉。</p>
<h3 id="finally代码块和finalize-方法有什么区别？"><a href="#finally代码块和finalize-方法有什么区别？" class="headerlink" title="finally代码块和finalize()方法有什么区别？"></a>finally代码块和finalize()方法有什么区别？</h3><p>无论是否抛出异常，finally代码块都会执行，它主要是用来释放应用占用的资源。finalize()方法是Object类的一个protected方法，它是在对象被垃圾回收之前由Java虚拟机来调用的。</p>
<h3 id="什么是Applet？"><a href="#什么是Applet？" class="headerlink" title="什么是Applet？"></a>什么是Applet？</h3><p>java applet是能够被包含在HTML页面中并且能被启用了java的客户端浏览器执行的程序。Applet主要用来创建动态交互的web应用程序。</p>
<h3 id="解释一下Applet的生命周期"><a href="#解释一下Applet的生命周期" class="headerlink" title="解释一下Applet的生命周期"></a>解释一下Applet的生命周期</h3><p>applet可以经历下面的状态：</p>
<p>Init：每次被载入的时候都会被初始化。</p>
<p>Start：开始执行applet。</p>
<p>Stop：结束执行applet。</p>
<p>Destroy：卸载applet之前，做最后的清理工作。</p>
<h3 id="当applet被载入的时候会发生什么？"><a href="#当applet被载入的时候会发生什么？" class="headerlink" title="当applet被载入的时候会发生什么？"></a>当applet被载入的时候会发生什么？</h3><p>首先，创建applet控制类的实例，然后初始化applet，最后开始运行。</p>
<h3 id="Applet和普通的Java应用程序有什么区别？"><a href="#Applet和普通的Java应用程序有什么区别？" class="headerlink" title="Applet和普通的Java应用程序有什么区别？"></a>Applet和普通的Java应用程序有什么区别？</h3><p>applet是运行在启用了java的浏览器中，Java应用程序是可以在浏览器之外运行的独立的Java程序。但是，它们都需要有Java虚拟机。</p>
<p>进一步来说，Java应用程序需要一个有特定方法签名的main函数来开始执行。Java applet不需要这样的函数来开始执行。</p>
<p>最后，Java applet一般会使用很严格的安全策略，Java应用一般使用比较宽松的安全策略。</p>
<h3 id="Java-applet有哪些限制条件？"><a href="#Java-applet有哪些限制条件？" class="headerlink" title="Java applet有哪些限制条件？"></a>Java applet有哪些限制条件？</h3><p>主要是由于安全的原因，给applet施加了以下的限制：</p>
<p>applet不能够载入类库或者定义本地方法。</p>
<p>applet不能在宿主机上读写文件。</p>
<p>applet不能读取特定的系统属性。</p>
<p>applet不能发起网络连接，除非是跟宿主机。</p>
<p>applet不能够开启宿主机上其他任何的程序。</p>
<h3 id="什么是不受信任的applet？"><a href="#什么是不受信任的applet？" class="headerlink" title="什么是不受信任的applet？"></a>什么是不受信任的applet？</h3><p>不受信任的applet是不能访问或是执行本地系统文件的Java applet，默认情况下，所有下载的applet都是不受信任的。</p>
<h3 id="从网络上加载的applet和从本地文件系统加载的applet有什么区别？"><a href="#从网络上加载的applet和从本地文件系统加载的applet有什么区别？" class="headerlink" title="从网络上加载的applet和从本地文件系统加载的applet有什么区别？"></a>从网络上加载的applet和从本地文件系统加载的applet有什么区别？</h3><p>当applet是从网络上加载的时候，applet是由applet类加载器载入的，它受applet安全管理器的限制。</p>
<p>当applet是从客户端的本地磁盘载入的时候，applet是由文件系统加载器载入的。</p>
<p>从文件系统载入的applet允许在客户端读文件，写文件，加载类库，并且也允许执行其他程序，但是，却通不过字节码校验。</p>
<h3 id="applet类加载器是什么？它会做哪些工作？"><a href="#applet类加载器是什么？它会做哪些工作？" class="headerlink" title="applet类加载器是什么？它会做哪些工作？"></a>applet类加载器是什么？它会做哪些工作？</h3><p>当applet是从网络上加载的时候，它是由applet类加载器载入的。类加载器有自己的java名称空间等级结构。类加载器会保证来自文件系统的类有唯一的名称空间，来自网络资源的类有唯一的名称空间。</p>
<p>当浏览器通过网络载入applet的时候，applet的类被放置于和applet的源相关联的私有的名称空间中。然后，那些被类加载器载入进来的类都是通过了验证器验证的。验证器会检查类文件格式是否遵守Java语言规范，确保不会出现堆栈溢出(stack overflow)或者下溢(underflow)，传递给字节码指令的参数是正确的。</p>
<h3 id="applet安全管理器是什么？它会做哪些工作？"><a href="#applet安全管理器是什么？它会做哪些工作？" class="headerlink" title="applet安全管理器是什么？它会做哪些工作？"></a>applet安全管理器是什么？它会做哪些工作？</h3><p>applet安全管理器是给applet施加限制条件的一种机制。浏览器可以只有一个安全管理器。安全管理器在启动的时候被创建，之后不能被替换覆盖或者是扩展。</p>
<h3 id="弹出式选择菜单-Choice-和列表-List-有什么区别"><a href="#弹出式选择菜单-Choice-和列表-List-有什么区别" class="headerlink" title="弹出式选择菜单(Choice)和列表(List)有什么区别"></a>弹出式选择菜单(Choice)和列表(List)有什么区别</h3><p>Choice是以一种紧凑的形式展示的，需要下拉才能看到所有的选项。Choice中一次只能选中一个选项。List同时可以有多个元素可见，支持选中一个或者多个元素。</p>
<h3 id="什么是布局管理器？"><a href="#什么是布局管理器？" class="headerlink" title="什么是布局管理器？"></a>什么是布局管理器？</h3><p>布局管理器用来在容器中组织组件。</p>
<h3 id="什么是JDBC？"><a href="#什么是JDBC？" class="headerlink" title="什么是JDBC？"></a>什么是JDBC？</h3><p>JDBC是允许用户在不同数据库之间做选择的一个抽象层。JDBC允许开发者用JAVA写数据库应用程序，而不需要关心底层特定数据库的细节。</p>
<h3 id="解释下驱动-Driver-在JDBC中的角色。"><a href="#解释下驱动-Driver-在JDBC中的角色。" class="headerlink" title="解释下驱动(Driver)在JDBC中的角色。"></a>解释下驱动(Driver)在JDBC中的角色。</h3><p>JDBC驱动提供了特定厂商对JDBC API接口类的实现，驱动必须要提供java.sql包下面这些类的实现：Connection, Statement, PreparedStatement,CallableStatement, ResultSet和Driver。</p>
<h3 id="Class-forName-方法有什么作用？"><a href="#Class-forName-方法有什么作用？" class="headerlink" title="Class.forName()方法有什么作用？"></a>Class.forName()方法有什么作用？</h3><p>初始化参数指定的类，并且返回此类对应的Class 对象</p>
<h3 id="PreparedStatement比Statement有什么优势？"><a href="#PreparedStatement比Statement有什么优势？" class="headerlink" title="PreparedStatement比Statement有什么优势？"></a>PreparedStatement比Statement有什么优势？</h3><p>PreparedStatements是预编译的，因此，性能会更好。同时，不同的查询参数值，PreparedStatement可以重用。</p>
<h3 id="什么时候使用CallableStatement？用来准备CallableStatement的方法是什么？"><a href="#什么时候使用CallableStatement？用来准备CallableStatement的方法是什么？" class="headerlink" title="什么时候使用CallableStatement？用来准备CallableStatement的方法是什么？"></a>什么时候使用CallableStatement？用来准备CallableStatement的方法是什么？</h3><p>CallableStatement用来执行存储过程。存储过程是由数据库存储和提供的。存储过程可以接受输入参数，也可以有返回结果。非常鼓励使用存储过程，因为它提供了安全性和模块化。准备一个CallableStatement的方法是：</p>
<p>CallableStatement Connection.prepareCall();</p>
<h3 id="数据库连接池是什么意思？"><a href="#数据库连接池是什么意思？" class="headerlink" title="数据库连接池是什么意思？"></a>数据库连接池是什么意思？</h3><p>像打开关闭数据库连接这种和数据库的交互可能是很费时的，尤其是当客户端数量增加的时候，会消耗大量的资源，成本是非常高的。可以在应用服务器启动的时候建立很多个数据库连接并维护在一个池中。连接请求由池中的连接提供。在连接使用完毕以后，把连接归还到池中，以用于满足将来更多的请求。</p>


    
    
    
	  <div class="full-width auto-padding tags">
      
        <a href="/tags/Java/">Java</a>
      
	  </div>
    
  </section>
</article>
    </div>
  
</section>


  <nav id="page-nav">
    
    <a class="prev" rel="prev" href="/page/4/">
      <span class="icon icon-chevron-left"></span>
      <span class="text">Previous</span>
    </a>
    
    
    <a class="next" rel="next" href="/page/6/">
      <span class="text">Next</span>
      <span class="icon icon-chevron-right"></span>
    </a>
    
  </nav>
  

      </div>
      <aside class='l_side'>
        
  <section class='m_widget links'>
<div class='header'>Links</div>
<div class='content'>
    <ul class="entry">
    
        <li><a class="flat-box" target="_blank" href="https://iiong.com/">
            <div class='name'>淮城一只猫</div>
        </a></li>
    
    </ul>
</div>
</section>

  <section class='m_widget categories'>
<div class='header'>Categories</div>
<div class='content'>
    
    <ul class="entry">
    
        <li><a class="flat-box" href="/categories/Android基础/"><div class='name'>Android基础</div><div class='badget'>1</div></a></li>
    
    </ul>
    
</div>
</section>

  
<div class="m_widget tagcloud">
    <div class="header">Tags</div>
    <div class='content'>
        <a href="/tags/Android/" style="font-size: 14px; color: #808080">Android</a> <a href="/tags/HTML/" style="font-size: 14px; color: #808080">HTML</a> <a href="/tags/Java/" style="font-size: 20px; color: #000">Java</a> <a href="/tags/Python/" style="font-size: 14px; color: #808080">Python</a>
    </div>
</div>



      </aside>
      <script>setLoadingBarProgress(60);</script>
    </div>
  </div>
  <footer id="footer" class="clearfix">

	<div class="social-wrapper">
  	
      
        <a href="https://github.com/stkevintan" class="social github"
          target="_blank" rel="external">
          <span class="icon icon-github"></span>
        </a>
      
        <a href="https://twitter.com/kevinsfork" class="social twitter"
          target="_blank" rel="external">
          <span class="icon icon-twitter"></span>
        </a>
      
        <a href="/atom.xml" class="social rss"
          target="_blank" rel="external">
          <span class="icon icon-rss"></span>
        </a>
      
    
  </div>
  
  <div>Theme <a href='https://github.com/stkevintan/hexo-theme-material-flow' class="codename">MaterialFlow</a> designed by <a href="http://keyin.me/" target="_blank">Kevin Tan</a>.</div>
  
</footer>


  <script>setLoadingBarProgress(80);</script>
  

<script src="//apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src='//cdn.bootcss.com/node-waves/0.7.5/waves.min.js'></script>
<script src="//cdn.bootcss.com/scrollReveal.js/3.3.2/scrollreveal.min.js"></script>
<script src="/js/jquery.fitvids.js"></script>
<script>
	var GOOGLE_CUSTOM_SEARCH_API_KEY = "";
	var GOOGLE_CUSTOM_SEARCH_ENGINE_ID = "";
	var ALGOLIA_API_KEY = "";
	var ALGOLIA_APP_ID = "";
	var ALGOLIA_INDEX_NAME = "";
  var AZURE_SERVICE_NAME = "";
  var AZURE_INDEX_NAME = "";
  var AZURE_QUERY_KEY = "";
  var BAIDU_API_ID = "";
  var SEARCH_SERVICE = "hexo";
  var ROOT = "/"||"/";
  if(!ROOT.endsWith('/'))ROOT += '/';
</script>
<script src="/js/search.js"></script>
<script src="/js/app.js"></script>


  <script>setLoadingBarProgress(100);</script>
</body>
</html>
